I have recently learned about acceptance testing with Page Objects . In this write-up I'd like to share with you how I learned to use those Page Objects with acceptance tests and how I have adopted them into component integration tests.

Acceptance testing

Let's start with an example acceptance test, one without the Page Object technique applied.

test( 'Creating a new to-do' , function (assert) { visit( '/todos/new' ); fillIn( 'input[name="todo_title"]' , 'Get milk' ); click( 'input[name="todo_submit"]' ); andThen(() => { assert.equal(currentURL(), '/todos' ); assert.ok(find( 'ul.todo-list li:contains("Get milk")' ).length > 0); }); });

The example tests that given you visit /todos/new , and you fill in a to-do title and then click on the submit button, then you will be redirected to /todos and a to-do with the given title should have been added to the list of to-dos.

As you can see the example contains information on how the create form for a to-do and how the list of to-dos are implemented. That's not something you would like to know at first from the acceptance test.

So let's take a look at how the former example will look be when we apply the Page Object pattern to hide all the implementation details:

test( 'Creating a new to-do' , function (assert) { let todo = new Todo( 'Get milk' ); visit( '/todos/new' ); todo.create(); andThen(() => { assert.equal(currentURL(), '/todos' ); assert.ok(todo.visible()); }); });

This example hides the implementation details behind the Todo Page Object, with the result that the test now reads much more as how a user would interact with your application.

Now if we take a look at the implementation of the Todo Page Object:

class Todo { constructor (title) { this .title = title; } create () { fillIn( 'input[name="todo_title"]' , this .title); click( 'input[name="todo_submit"]' ); } visible () { return find(` ul.todo-list li:contains("${ this .title}")` ).length > 0; } }