# Test Isolation aka How Wrong am I Doing It?

**URL:** <https://discuss.emberjs.com/t/test-isolation-aka-how-wrong-am-i-doing-it/7162>\
**Category:** Testing\
**Created:** [January 26, 2015, 6:03pm UTC](https://discuss.emberjs.com/t/test-isolation-aka-how-wrong-am-i-doing-it/7162 "2015-01-26T18:03:07Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![nikz](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/nikz/32/9723_2.png) [@nikz](https://discuss.emberjs.com/u/nikz)\
**Post date:** [January 26, 2015, 6:03pm UTC](https://discuss.emberjs.com/t/test-isolation-aka-how-wrong-am-i-doing-it/7162/1 "2015-01-26T18:03:07Z")

</div>

Hey Team,

I’m having a bunch of trouble with the learning curve on ember-cli testing.

Basically, it seems like Ember (ember-cli?) is super committed to isolated Unit tests - that is, tests with minimal dependencies. Definitely sounds like a recipe for speed and correct tests 😄

Perhaps it’s because I’m used to Rails, but I’m getting the feeling that the tests are over-isolated - that is, there could/should be some pragmatic additions that wouldn’t harm speed or nicely-split up testing, but would make things Just Work™ a little more. I’m happy to be proved wrong of course!

As a concrete example, pretend you’re testing what I’m guessing is a fairly standard Route:

```
import Ember from 'ember';

export default Ember.Route.extend({

  model: function() {
    return this.store.find("tutorial");
  },

  actions: {
    createTutorial: function() {
      var tut = this.store.createRecord("tutorial");
      tut.save().then( () => {
        this.transitionTo('whatever-route', tut);
     });
    return tut;
    }

  }

});

```

So you’ve got an action, and then you want to test it (I’ll omit most of the test boilerplate)

```
test('I can create a tutorial', function() {
  var route = this.subject();
  this.subject.get('actions').createTutorial();
  // assert something maybe...
}

```

Except that doesn’t work (My Ember-fu is not strong enough to figure out why actions is undefined, no matter what way I try to access it - if someone could enlighten me I’d be very appreciative!)

The [docs suggest separating concerns](http://emberjs.com/guides/testing/testing-routes/) to define an \_createTutorial method, and call that from within actions… which I’m really not sure I buy (how often are they going to be legitimately separate concerns? My example must be pretty common, and I can’t think of another reason for something else in my Route to be creating a Tutorial… when am I going to want to do something from a User action but do it a different way internally?) but anyway, I’ll do that…

```
 test('I can create a tutorial', function() {
  var route = this.subject();
  this.subject._createTutorial();
  // assertions?
}

```

Which results in:

`TypeError: Cannot read property 'createRecord' of undefined`

Oh. For some reason the route doesn’t have a DS.Store injected like my controllers in action do ☹

A quick google reveals an [awesome presentation](http://www.slideshare.net/bantic/ember-testing-internals-with-ember-cli) that reveals that `moduleForModel` adds `this.store()` (slide #23). But the Route test is just using `moduleFor`, presumably in the interests of isolation. There are also a lot of people using the dependency injection (i.e looking up store:main on `App. __container__ ` or similar, which seems hacky to me…?)

I understand the argument, but just having an in-memory (Fixture?) store feels like it would smooth out the learning curve there a bit - given that (again, as I understand it) the role of a Route is to hook user actions into your data store.

I apologise profusely if I happen to have just messed something up, please correct me 😊

tl;dr

1. How do I get access to a DS.Store instance in a Unit test for a Route?
2. Would there be an interest in having a moduleForRoute or some similar kind of “base” set of assumptions per test type that makes a simple test a bit more frictionless?

Thanks,

Nik

---

<div class="post-metadata">

**Author:** ![jgwhite](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/jgwhite/32/3678_2.png) [@jgwhite](https://discuss.emberjs.com/u/jgwhite)\
**Post date:** [January 26, 2015, 7:33pm UTC](https://discuss.emberjs.com/t/test-isolation-aka-how-wrong-am-i-doing-it/7162/2 "2015-01-26T19:33:35Z")

</div>

For this kind of test I reckon the important thing is to assert that your route is sending the _expected messages_.

Sandi Metz says it best in [Magic Tricks of Testing](https://speakerdeck.com/skmetz/magic-tricks-of-testing-railsconf),

With this in mind, we want to inject mock collaborators with which we can assert that our route is sending the right messages:

```nohighlight
test('foo', function() {
  // set up our collaborators

  var route = this.subject();

  var didCreate;
  var didSave;
  var didTransition;

  var mockRecord = {
    save: function() {
      didSave = true; // record that we called model.save
      return Promise.resolve();
    }
  };

  var mockStore = {
    createRecord: function(type) {
      didCreate = true; // record that we called store.createRecord
      equal(type, 'tutorial',
        'expected createRecord to be called with "tutorial"');
      return mockRecord;
    }
  };

  // inject our collaborators

  route.store = mockStore;

  // spy on one of our own methods

  route.transitionTo = function mockTransitionTo(route, model) {
    didTransition = true; // record that we called transitionTo
    equal(route, 'foo',
      'expected transitionTo to be called with "foo"');
    equal(model, mockRecord,
      'expected transitionTo to be called with the right model');
  }

  // call the method under test

  route.send('createTutorial');

  // verify the right things happened

  ok(didCreate, 'expected to call store.createRecord');
  ok(didSave, 'expected to call model.save');
  ok(didTransition, 'expected to transition');
});

```

This test is a little overwrought but demonstrates the various ways to assert behaviour.

One thing that struck me as unintuitive is that we invoke the action by calling `send('createTutorial')`. I guess this could do with clearer explanation but the details can be found in [the docs for ActionHandler](http://emberjs.com/api/classes/Ember.ActionHandler.html#method_send).

You have to be careful with this kind of test not to simply reproduce the internal implementation but hopefully this demonstrates some of the tools available.

---

<div class="post-metadata">

**Author:** ![nikz](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/nikz/32/9723_2.png) [@nikz](https://discuss.emberjs.com/u/nikz)\
**Post date:** [January 26, 2015, 9:01pm UTC](https://discuss.emberjs.com/t/test-isolation-aka-how-wrong-am-i-doing-it/7162/3 "2015-01-26T21:01:38Z")

</div>

OK - thanks very much for that 🙂 The `send()` tip is definitely a big help.

Your example actually illustrates my argument quite well I think. I understand that the idea is complete isolation, but I think it’s gone a bit far when you have to mock out a whole bunch of common collaborators.

For instance:

- There are already `currentRoute()` helpers (and variants thereof). In tests, couldn’t we just automatically mock out `transitionTo` to set the correct class state for us to test?
- Similarly, given that it’s going to be very common that you’re testing some kind of storage, why not an automatic/fixture store?

In short, is it worth pulling back a little into the “integrated” direction and in the process ditching a bit of the test “bookkeeping” - It Just Works™ (for most of the people most of the time)

Maybe I should try putting it together as an add-on 😉
