Unit Testing Ember Data Models

Unit testing Ember Data models seems to involve a bit of boilerplate right now. For example, a simple test for a computed property ends up looking like this:

module('User');

test('displayName', function() {
  var store = App.__container__.lookup('store:main'),
      model;

  Ember.run(function() {
    model = store.createRecord('user', {
      id: 1,
      firstName: 'Tom',
      lastName: 'Dale'
    });
  });

  equal(model.get('displayName'), 'Tom Dale');
});

So in this case we’re reaching into the __container__ cookie jar. Another trick to get things going would be to use another supposedly private piece of the system, _create:

module('User');

test('displayName', function() {
  var model = App.User._create({
    firstName: 'Tom',
    lastName: 'Dale'
  });

  equal(model.get('displayName'), 'Tom Dale');
});

Neither of these approaches seem ideal.

Anyone got any suggestions for going about this? And, perhaps more importantly, explaining it to newcomers to the framework?

2 Likes

For most of my computed properties and specific behaviour on models, I moved the funcitonnality to a Mixin like this.

App.UserMixin = Ember.Mixin.create({
  fullName: function () {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});
  
App.User = DS.Model.extend(App.UserMixin, {
  firstName: DS.attr('string'),
  lastName: DS.attr('string')
});
  
// -------------------
  
describe('User', function () {
  var User = Ember.Object.extend(App.UserMixin);
  it('concat the first name and the last name into the full name', function () {
    var user = User.create({
      firstName: 'Tom',
      lastName: 'Dale'
    });
    expect(user.get('lastName')).to.equal('Tom Dale');
  });
})

This tests only your computed property and ensures your record behave correctly. This is not ideal for ember-data specific custom behaviours but it works for the most part.

2 Likes

@Floby that’s a nice hack-free solution.

Here’s another decoupled approach:

module('User');

test('displayName', function() {
  var user = new App.User();

  user.setProperties({
    firstName: 'Tom',
    lastName: 'Dale'
  });

  equal(user.get('displayName'), 'Tom Dale');
});

UPDATE

This approach leads to unfortunate Attempted to handle event didSetProperty ... while in state root.empty errors :frowning:

Looks like the mixin approach is probably the safest for disentangled unit tests.

If you look at Ember Data’s own tests, you can find a handy function in tests/ember_configuration.js called setupStore. This function sets up a new “Container” and registers the necessary bits: a store, adapters, serializers, and model definitions.

This method is used in the Ember Data unit tests, so you can see how it is used.

I was pretty amazed when I saw how integrated the unit tests were, but now I see that isolation is something that still needs a lot of work and we’ll probably be stuck with highly integrated tests for a long while.

Agreed.

I guess the question is: what would we like these tests to look like? Or, moreover, what would a new user expect?

Probably this:

module('User');

test('displayName', function() {
  var user = App.User.create({
    firstName: 'Tom',
    lastName: 'Dale'
  });

  equal(user.get('displayName'), 'Tom Dale');
});

But is this achievable without sacrificing robustness and functionality?

I think I can speak to this as a newcomer to the framework, @jgwhite. What you suggested someone new to the framework was right on. And although it is disappointing to have to open up the App.__container__ business, I was struggling to find an example of testing an Ember Data-backed model, and your example helped a lot. Thanks.

@thenickcox glad to hear that example helped.

The new testing guides from @cavneb and co are due to land really soon. They’ll become the go-to resources for this topic.