Ember.js TDD – how to test your ember views

I’ve been playing around with ember.js and was searching for a good example on how to fully unit test a project with jasmine and sinon.

I haven’t found a central project which handle this issue completely, and I needed to collect bits and pieces from different projects all around github and other blog posts.

So I decided to open up a new github project, called emberjs-tdd, which I hope will be the central repository for unit and integration tests best practices.

A word about TDD

Working without unit test to cover your back in a javascript project can be an expensive decision if you care about lowering your code complexity and maintenance time.
I won’t get into the benefits of TDD or BDD because others have explained it far better than me.

 

Setup the environment

For organizing my projects I’m using requireJs, and for testing I’m using jasmine.

All of the bootstraping can be found on the TestRunner.js file under the “spec” folder.

 

Testing ember.js views

To begin our testing driven development a view, we’ll start with a simple login page view, so lets create a new file called “LoginViewSpec“ .

First, we want to setup our beforeEach and afterEach methods, and test that this view is defined at all, so we do:

describe("Given a login view it", function(){
   var loginView = null;

 beforeEach(function(){
    loginView = LoginView.create();

   Ember.run(function(){
      loginView.append();
   });
 });

 afterEach(function(){
   Ember.run(function(){
     loginView.remove();
   });
   loginView = null;
 });

 it("Should be defined", function(){
   expect(loginView).toBeDefined();
 });

 

The reason behind using “Ember.run” can be found on this post.

Now we run the TestRunner.html, and we see 1 test failing, so we need to create an empty LoginView.

We want to add a new input field to the view with a “userInput” class, to hold the username value, so we’ll need to add a test for it.

To test elements on our ember view, we can use the native jquery dependency ember views have with the View.$(…) method:

it("Should have an user input", function(){
   expect(loginView.$("input.userInput").length).toEqual(1);
 });

We run the test, the test fails. Good, now we’ll need to add the proper View code and the actual input to the LoginTemplate.handlebars file, which the LoginView is loading as it’s template.

So for the view code:

define(["text!templates/LoginView.handlebars","ember"],function(LoginTemplate){
var loginView = Em.View.extend({
      template: Em.Handlebars.compile(LoginTemplate),
   });
 return loginView;
});

And for the template code:

{{view Ember.TextField class="userInput"}}

Testing bindings

One dilemma I had was how to test ember bindings inside of templates. Should I regex for  ”App.View.content” on the template text, or should I do something more flexible.

I ended up choosing to test my view bindings with a homemade mock object as the data value in the view, and to check that everything is connected correctly.

So I added a test that checks that I have a user propery on my view:

it ("Should have a user property", function(){
  expect(loginView.get("user")).toBeDefined();
});

Watched the test fail and added the user property to the view:

var loginView = Em.View.extend({
 template: Em.Handlebars.compile(LoginTemplate),
 user: null
})

And the tests pass again… great success!

Now I’ve added another test to test the binding:

it("Should bind the username input value to the user.name property", function(){
  Ember.run(function(){
     loginView.set("user", Em.Object.create({name:"mockValue"}));
  });
  expect(loginView.$("input.userInput").val()).toEqual("mockValue");
});

Run the test – test test fails as it should, now we’ll need to add the bindings, which in ember, is one line of magic (this is what I love about ember…):

{{view Ember.TextField valueBinding="user.name"}}

Now the test pass for the last time.

Few more controls, and our login view is ready to go, the full version can be viewed on the emberjs-tdd project.

Note about testing views and their templates

In classic unit testing, you must test a unit of code, without any dependencies. In our case, we are testing the template element structure in our View test.

I had a lot of concern about this approach, because proper unit testing, as far as I know would be to unit test the template, and unit test the view separately without any assumptions.

I didn’t find an elegant paradigm of template testing (because you don’t want to test too much parts that are constantly change, like templates or css) but to preserve proper TDD you must test something or you’ll lose test  coverage.

So if someone have got a cleaner way of doing this properly I’ll be more than happy to hear about it, or better yet get a pull request to update the emberjs-tdd project.

For now I’m guessing that the test above will work just fine as long as your template preserve the structure of having an input element with a class of “userInput”.

If you find out about other good practices on how to TDD your ember projects, please contribute to the emberjs-tdd project so we can all have a solid convention on how to do it.

Hope it helps anyone out there,

Shai

4 comments

  1. Hi,
    I get the github project to test on it, but when I open the TestRunner.html I noted that there wasn’t any test pass.
    When I read the code, I figured out that LoginViewSpec.js has the test “inside” the declaration of the require function, which is omitting the test.
    Have you found a way to run that test using require.js ?
    Thanks in advance,

    • It works as long as you run it on a server http protocol.
      Using the file:// protocol causes problem when loading through require.js.

  2. Hi, I’m trying to unit-test ember events in my views right now. In particular, I have an {{action}} helper that is supposed to trigger some function on click. It works fine in production. However, when in unit testing, I try to trigger the event with myView.$().trigger(‘click’) and nothing happens. I’m wondering if you have any tips for testing clicks and events.

    Thanks!

    • Hi Sherwin,
      I encountered the same problem myself and decided to regexp the html to make sure I have the right action setup.

      But… since this post I moved from the ember world to AngularJS where it is much more testing friendly, designed with TDD in mind, and everything is tested easily. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

HTML tags and attributes are not allowed.