ember.jsqunitember-qunit

Service not injected into component's integration test


I have a service Foo (e.g. app/services/foo.js):

import Ember from 'ember';

const FooService = Ember.Service.extend({
  myMethod() { ... }
});

export default FooService;

which I initialize in app's initializers (e.g. app/initializers/foo.js), such as:

const initialize = function initialize(app) {
  app.inject('component', 'foo', 'service:foo');
}

const FooInitializer = {
  name: 'foo',
  initialize: initialize
}

export {initialize}
export default FooInitializer;

And then I have a component (e.g. app/components/my-component.js), in which I use this service (I do not inject it manually here because it's already injected into every component in initializers):

import Ember from 'ember'

const MyComponent = Ember.Component.extend({

  actions:
    doSomething() { this.get('foo').myMethod(); }

});

export default MyComponent;

I created integration test for this component:

import { test, moduleForComponent } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';

const fooStub = Ember.Service.extend({
  myMethod() {
    return true;
  }
});

moduleForComponent('my-component', 'Integration | Component | my-component', {
  integration: true,

  beforeEach() {
    this.register('service:foo', fooStub);
    // Calling inject puts the service instance in the context of the test,
    // making it accessible as "foo" within each test
    this.inject.service('foo', { as: 'foo' });
  }
});

test('it does something', function (assert) {
  this.render(hbs`{{my-component}}`);
  const spy = sinon.spy(this.get('foo'), 'myMethod');

  const $someElement = this.$().find('.some-element-within-my-component');
  // This element triggers `doSomething` action inside the component
  $someElement.click();

  assert.ok(spy.calledOnce, "myMethod was called within foo service");
});

When running this test, it throws an error:

TypeError: Cannot read property 'myMethod' of undefined

which means the service is not being injected, even thought I injected it in the test manually as a stub.

I read a couple of discussions, but none was really helpful until I bumped into this one which hinted me that Ember might not inject services into tests if they are initialized using initializers and not injected manually into the component that is being tested.

So I tried to inject the service manually into the component and the test worked. This is, however, only partial solution since it completely destroys the purpose of initializers if I have to inject services into my components (and there's a lot of them) manually just to make the tests work.

Have any of you experienced this and if so, is there something I'm doing wrong or is there a workaround to make this work without manually injecting my services into every component that I have? Maybe this will end up filing a bug in Ember, but I first wanted to try stackoverflow to see if there's another solution.


Solution

  • AFAIK, initializers and instance initializers only runs in acceptance-testing. Hence; any injection made inside the initializers will have to be handled manually for integration-testing. However; IMHO this does not mean you have to change your whole design and inject the service to the component manually so that the test should pass. Why do not just pass the stub service you created to the component while rendering the component? I mean the following:

    this.render(hbs`{{my-component foo=foo}}`);
    

    This simply passes the stub service to the component. In the end what you want is a way to pass the service to the component from outside (either through an initializer, which does not run in integration-testing automatically, or through template). This might not be the ideal solution you would like to have; but it is good enough to keep your code as you wish with much less fuss.