How to mock an Ember-CLI service in an acceptance test?

前端 未结 3 1332
北荒
北荒 2021-01-31 16:51

Quick summary/tldr:

  • It seems that Ember\'s container lookup process + Ember-CLI\'s module resolver doesn\'t allow manually un-registering a service and then regi
相关标签:
3条回答
  • 2021-01-31 17:13

    Short version of the solution: your registered mock service must have a different service:name than the "real" service you're trying to mock.

    Acceptance test:

    import Ember from 'ember';
    import { module, test } from 'qunit';
    import startApp from 'container-doubling/tests/helpers/start-app';
    
    var application;
    
    let speakerMock = Ember.Service.extend({
      speak: function() {
        console.log("Acceptance Mock!");
      }
    });
    
    module('Acceptance | acceptance demo', {
      beforeEach: function() {
        application = startApp();
    
        // the key here is that the registered service:name IS NOT the same as the real service you're trying to mock
        // if you inject it as the same service:name, then the real one will take precedence and be loaded
        application.register('service:mockSpeaker', speakerMock);
    
        // this should look like your non-test injection, but with the service:name being that of the mock.
        // this will make speakerService use your mock
        application.inject('component', 'speakerService', 'service:mockSpeaker');
      },
    
      afterEach: function() {
        Ember.run(application, 'destroy');
      }
    });
    
    test('visit a route that will trigger usage of the mock service' , function(assert) {
      visit('/');
    
      andThen(function() {
        assert.equal(currentURL(), '/');
      });
    });
    

    Integration test (this is what I was originally working on that caused me issues)

    import { moduleForComponent, test } from 'ember-qunit';
    import hbs from 'htmlbars-inline-precompile';
    import Ember from 'ember';
    
    
    let speakerMock = Ember.Service.extend({
      speak: function() {
        console.log("Mock one!");
      }
    });
    
    moduleForComponent('component-one', 'Integration | Component | component one', {
      integration: true,
    
      beforeEach: function() {
        // ember 1.13
        this.container.register('service:mockspeaker', speakerMock);
        this.container.injection('component', 'speakerService', 'service:mockspeaker');
    
        // ember 2.1
        //this.container.registry.register('service:mockspeaker', speakerMock);
        //this.container.registry.injection('component', 'speakerService', 'service:mockspeaker');
      }
    });
    
    test('it renders', function(assert) {
      assert.expect(1);
    
      this.render(hbs`{{component-one}}`);
    
      assert.ok(true);
    });
    
    0 讨论(0)
  • 2021-01-31 17:25

    You can register your mock and inject it instead of the original service.

    application.register('service:mockLogger', mockLogger, {
      instantiate: false
    });
    
    application.inject('route', 'loggerService', 'service:mockLogger');
    application.inject('controller', 'loggerService', 'service:mockLogger');
    

    I use this approach for mocking the torii library in my third-party login acceptance tests. I hope there will be a nicer solution in the future.

    0 讨论(0)
  • 2021-01-31 17:29

    The existing answers work well, but there's a way that avoids renaming the service and skips the inject.

    See https://github.com/ember-weekend/ember-weekend/blob/fb4a02353fbb033daefd258bbc032daf070d17bf/tests/helpers/module-for-acceptance.js#L14 and usage at https://github.com/ember-weekend/ember-weekend/blob/fb4a02353fbb033daefd258bbc032daf070d17bf/tests/acceptance/keyboard-shortcuts-test.js#L13

    I'll present it here as an update to the test helper I previously had here, so it's a drop-in replacement, but you may just want to follow the links above instead.

    // tests/helpers/override-service.js
    // Override a service with a mock/stub service.
    // Based on https://github.com/ember-weekend/ember-weekend/blob/fb4a02353fbb033daefd258bbc032daf070d17bf/tests/helpers/module-for-acceptance.js#L14
    // e.g. used at https://github.com/ember-weekend/ember-weekend/blob/fb4a02/tests/acceptance/keyboard-shortcuts-test.js#L13
    //
    // Parameters:
    // - newService is the mock object / service stub that will be injected
    // - serviceName is the object property being replaced,
    //     e.g. if you set 'redirector' on a controller you would access it with
    //     this.get('redirector')
    function(app, newService, serviceName) {
      const instance = app.__deprecatedInstance__;
      const registry = instance.register ? instance : instance.registry;
      return registry.register(`service:${serviceName}`, newService);
    }
    

    Plus performing the jslint and helper registration steps from https://guides.emberjs.com/v2.5.0/testing/acceptance/#toc_custom-test-helpers

    I can then call it like this, in my example stubbing out a redirect (window.location) service, which we want to do because redirecting breaks Testem:

    test("testing a redirect's path", function(assert) {
      const assertRedirectPerformed = assert.async();
      const redirectorMock = Ember.Service.extend({
        redirectTo(href) {
          assert.equal(href, '/neverwhere');
          assertRedirectPerformed();
        },
      });
    
      overrideService(redirectorMock, 'redirector');
      visit('/foo');
      click('#bar');
    });
    
    0 讨论(0)
提交回复
热议问题