如何在验收测试中模拟Ember-CLI服务? [英] How to mock an Ember-CLI service in an acceptance test?

查看:68
本文介绍了如何在验收测试中模拟Ember-CLI服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  • It seems that Ember's container lookup process + Ember-CLI's module resolver doesn't allow manually un-registering a service and then registering a replacement if the original service can be resolved using the resolver (I want to do the method described here, but it doesn't work)
  • How can I mock an Ember-CLI service in an acceptance test without using a hacky, custom resolver? (example project/acceptance test here)
ember generate service logger

services / logger.js

export default Ember.Object.extend({
  log: function(message){
    console.log(message);
  }
});

initializers / logger-service.js

export function initialize(container, application) {
  application.inject('route', 'loggerService', 'service:logger');
  application.inject('controller', 'loggerService', 'service:logger');
}

该服务通过注入的名称访问, loggerService ,在应用程序控制器的操作处理程序中:

The service is accessed through its injected name, loggerService, in an action handler on the application controller:

templates / application.hbs

<button id='do-something-button' {{action 'doSomething'}}>Do Something</button>

controllers / application.hs

export default Ember.Controller.extend({
  actions: {
    doSomething: function(){
      // access the injected service
      this.loggerService.log('log something');
    }
  }
});



尝试测试此行为是否正确发生



我创建了验收测试,检查按钮点击触发了该服务。意图是嘲笑服务,并确定是否被调用而不实际触发服务的实现 - 这样可以避免实际服务的副作用。

Attempt to test that this behavior occurs correctly

I created an acceptance test that checks that the button click triggered the service. The intent is to mock out the service and determine if it was called without actually triggering the service's implementation -- this avoids the side-effects of the real service.

ember generate acceptance-test application

测试/验收/应用程序-test.js

import Ember from 'ember';
import startApp from '../helpers/start-app';

var application;
var mockLoggerLogCalled;

module('Acceptance: Application', {
  setup: function() {

    application = startApp();

    mockLoggerLogCalled = 0;
    var mockLogger = Ember.Object.create({
      log: function(m){
        mockLoggerLogCalled = mockLoggerLogCalled + 1;
      }
    });

    application.__container__.unregister('service:logger');
    application.register('service:logger', mockLogger, {instantiate: false});

  },
  teardown: function() {
    Ember.run(application, 'destroy');
  }
});

test('application', function() {
  visit('/');
  click('#do-something-button');
  andThen(function() {
    equal(mockLoggerLogCalled, 1, 'log called once');
  });
});

这是基于谈话 em emo应用程序:管理依赖关系 通过mixonic,建议取消注册现有服务,然后重新注册嘲笑版本: / p>

This is based on the talk Testing Ember Apps: Managing Dependency by mixonic that recommends unregistering the existing service, then re-registering a mocked version:

application.__container__.unregister('service:logger');
application.register('service:logger', mockLogger, {instantiate: false});

不幸的是,使用Ember-CLI,这不起作用。罪魁祸首是Ember容器中的此行

Unfortunately, this does not work with Ember-CLI. The culprit is this line in Ember's container:

function resolve(container, normalizedName) {
  // ...
  var resolved = container.resolver(normalizedName) || container.registry[normalizedName];
  // ...
}

这是容器查找的一部分链。问题是容器的解析方法检查解析器之前检查其内部注册表 application.register 命令使用容器的注册表注册我们的嘲笑服务,但是当解析在查询注册表之前,将调用与解析器的容器检查。 Ember-CLI使用自定义的解析器来匹配模块的查找,这意味着它将始终解析原始模块,而不使用新注册的模拟服务。这种变通方法看起来很可怕,涉及到修改解析器,从来不会找到原始服务的模块,这样就允许容器使用手动注册的模拟服务。

which is part of the container's lookup chain. The issue is that the container's resolve method checks the resolver before checking its internal registry. The application.register command registers our mocked service with the container's registry, but when resolve is called the container checks with the resolver before it queries the registry. Ember-CLI uses a custom resolver to match lookups to modules, which means that it will always resolve the original module and not use the newly registered mock service. The workaround for this looks horrible and involves modifying the resolver to never find the original service's module, which allows the container to use the manually registered mock service.

使用自定义解析器测试允许服务成功嘲笑。这通过允许解析器执行正常查找,但是当查询我们的服务名称时,修改的解析器的行为就像没有匹配该名称的模块。这导致解析方法在容器中找到手动注册的模拟服务。

Using a custom resolver in the test allows the service to be successfully mocked. This works by allowing the resolver to perform normal lookups, but when our service's name is looked up the modified resolver acts like it has no module matching that name. This causes the resolve method to find the manually registered mock service in the container.

var MockResolver = Resolver.extend({
  resolveOther: function(parsedName) {

    if (parsedName.fullName === "service:logger") {
      return undefined;
    } else {
      return this._super(parsedName);
    }
  }
});

application = startApp({
  Resolver: MockResolver
});



这似乎不是必要的,与上述提到的服务嘲笑不符幻灯片。是否有更好的方式来模拟这项服务?



此问题中使用的ember-cli项目可以在 github上的此示例项目

推荐答案

解决方案的简短版本:您的注册模拟服务必须具有不同的服务:名称,而不是您想要嘲笑的真实服务。

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

验收测试:

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);
});

这篇关于如何在验收测试中模拟Ember-CLI服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆