Ember-testing is a recent addition to the Ember.js framework that allows you to create integration tests for your app using a custom DSL for interacting with your async Ember.js app. We’re going to walk through how to write integration tests with ember-testing using the qunit-rails gem.
Lets all take a moment to thank Erik Bryn who did the lions share of the work on ember-testing. You can see him talking about it over here with slides and an example app
Lets assume that we have a small library of books and we’ve written an app to keep track of them. The app simply allows us to save some basic details about each book and allocate tags. It didn’t take long to get the app up and running but we’re in the wild west of coding with no tests and the librarian is concerned that the app might have bugs in it. We need to prove it works.
One of the best things about the qunit-rails gem is that you can see the integration tests being run right in front of you. Not only will you see the library app in the bottom right corner of the page but you will see integration tests being run against that instance. You can see the tests in action below or run them yourself here. Excited yet? I know I was.
To get the started you need to add the qunit-rails to your Gemfile, install your bundle and run the following generator to create the qunit test-helper. You can add a -c param if you’re into coffeescript.
This prepares the Ember.js app for testing and renders it into a div in the bottom right corner of the test page as well as declaring a useful helper function to determine whether elements exist in the application.
This declares a module with one test, which loads the root url of the app and checks that something/anything is returned.
If you’re following along you can fire up your rails server now and browse to localhost:3000/qunit . You should see a single test that has been run and your app in the bottom right hand corner.
Thats all the setup done. Now lets build up the suite of integration tests but first lets talk about that simple test. Qunit allows you to define a module which groups similar tests together. At the moment we have one module and one test. The setup and teardown functions are called by qunit before and after EACH test in the module. In the setup function we tell the Ember.js app to move into a ready state and in the teardown function we reset Ember.App after each test as described here.
The actual test calls visit(“/”) to retrieve the home page and then asserts that the app has indeed returned something, but what are all these functions?
- The visit function belongs to the ember-testing DSL, It tells the test to load a url in the app. There a few other ember-testing DSL functions…I’ll talk about them later.
- The ok function is a qunit assertion which checks that the boolean condition passed to it evaluates to true. You can find more qunit assertions here
- The exists function takes a jQuery selector and returns true if it exists and throws an exception if it doesn’t. We defined it in the test_helper.js class up above
On line 12 in the simple test above we use the visit function to retrieve the root url of the app. This is followed by a call to the then function with an anonymous function describing the test. The visit function is a promise. This means that once the contents of that url have been retrieved or the promise fulfilled, then the anonymous function will be executed. We will continue to use this pattern for the tests.
The Ember-testing DSL currently consists of the following functions:
- visit (url): This will point the ember application to the specified url
- click (selector, context): This will click the element described by the jQuery selector. A context for the jQuery selector can also optionally be supplied
- fillIn (selector, context, text): This will update the value of the element described by the jQuery selector with the supplied text. A context for the selector can be optionally supplied.
- find (selector, context): This will retrieve the element identifie by the jQuery selector. A context for the selector can be optionally supplied.
So.. we have an ember-testing DSL for interacting with the app under test and some qunit assertions to check that the behaviour is as expected. Lets write some tests!… We’ll start off with a test to make sure that the contents of the Library is as expected:
The first thing we do in this test is we tell qunit to expect 6 assertions. Then we vist the root url / home pag and check that the Nav bar div element is present and that the search button was rendered.
Next up we click the search button and pass in an anonymous function to continue testing the results page once the App is ready. Note how easy it is to deal with th async nature of our Ember.js applications.
We then confirm that the library has three books in it and that the three titles correspond to our seed data, which is really just the set of fixtures being accessed via the ember-data fixture adapter.
So far we’ve used the visit, click and find functions from the ember-testing DSL. Lets write a test to edit the title of a book in the library so that we can use the fillIn function.
In this test, we search for all the books in the library and then check that there is no book called ‘The Great Modification’. We use the qunit throws assertion to ensure that an exception is thrown when we attempt to find a search result with this title.
We then click the link on the book ‘The Great Gatsby’ and load up the show page where we check that the book has the correct tags associated with it and then click the link to edit the book.
Now, on the edit page we check that the book still has the correct title ‘The Great Gatsby’. This is contained in the input box with class ‘titleInputBox’ in the div with class ‘editBook’
We then use the fillIn function to change the value of the input box to the new title ‘The Great Modification’, and for the sake of rigour we validate that this input box now has been indeed been changed to the new title. Happy with this we click the button to save changes to the book and then click the search button again.
Lastly we confirm that ‘The Great Gatsby’ is no longer in the search results, but ‘The Great Modification is.
Success! If you haven’t done so yet, why not try and interact with the application in the bottom right hand corner. You will see the modified book there. The app is fully functional just scaled down in size.
In case you are wondering why the modified title is still there, despite us calling App.reset() in the teardown() function.. it is because only the Ember app is reset. This example has been configured to use the FixtureAdapter from ember-data which is not reset. If you reload the page, the results will revert to the seed defined as fixtures and the tests will pass again.
Ember-data is completely optional for creating ember-testing integration tests. I just found it useful to use the FixtureAdapter to create the seed data. You could use the RestAdapter if you wanted your integration tests to go all the way down to the server and database, but then you would need to come up with a mechanism for ensuring that your data is in a consistent state when you start running the tests.
Hopefully this will get you up and running with ember-testing. Good luck!
The source code for the rails app and tests is available here