resolver
解析原始服务(我想做)方法described here,但它不起作用)ember generate service logger
服务/ logger.js
export default Ember.Object.extend({
log: function(message){
console.log(message);
}
});
初始化/记录器-service.js
export function initialize(container, application) {
application.inject('route', 'loggerService', 'service:logger');
application.inject('controller', 'loggerService', 'service:logger');
}
该服务通过其注入名称loggerService
在应用程序控制器上的操作处理程序中访问:
模板/ application.hbs
<button id='do-something-button' {{action 'doSomething'}}>Do Something</button>
控制器/ application.hs
export default Ember.Controller.extend({
actions: {
doSomething: function(){
// access the injected service
this.loggerService.log('log something');
}
}
});
我创建了一个验收测试,用于检查按钮单击是否触发了服务。目的是模拟服务并确定是否在没有实际触发服务实现的情况下调用它 - 这样可以避免实际服务的副作用。
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');
});
});
这是基于mixonic的谈话Testing Ember Apps: Managing Dependency,建议取消注册现有服务,然后重新注册模拟版本:
application.__container__.unregister('service:logger');
application.register('service:logger', mockLogger, {instantiate: false});
不幸的是,使用Ember-CLI 这不起作用。罪魁祸首是Ember容器中的this line:
function resolve(container, normalizedName) {
// ...
var resolved = container.resolver(normalizedName) || container.registry[normalizedName];
// ...
}
是容器查找链的一部分。问题是容器的resolve
方法在检查其内部resolver
之前检查registry
。 application.register
命令使用容器的registry
注册我们的模拟服务,但是当调用resolve
时,容器会在resolver
查询registry
之前检查resolver
。 Ember-CLI使用自定义resolver
来匹配模块的查找,这意味着它将始终解析原始模块而不使用新注册的模拟服务。对此的解决方法看起来很糟糕,并且涉及修改resolver
以永远不会找到原始服务的模块,这允许容器使用手动注册的模拟服务。
在测试中使用自定义resolve
可以成功模拟服务。这通过允许解析器执行正常查找来工作,但是当查找我们的服务名称时,修改后的解析器就像没有与该名称匹配的模块一样。这会导致var MockResolver = Resolver.extend({
resolveOther: function(parsedName) {
if (parsedName.fullName === "service:logger") {
return undefined;
} else {
return this._super(parsedName);
}
}
});
application = startApp({
Resolver: MockResolver
});
方法在容器中找到手动注册的模拟服务。
{{1}}
此问题中使用的ember-cli项目可在this example project on github.
中找到答案 0 :(得分:17)
解决方案的简短版本:您注册的模拟服务必须具有不同的服务:名称而不是&#34;真实&#34;为你服务并试图嘲笑。
验收测试:
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(), '/');
});
});
集成测试(这是我最初的工作,这导致了我的问题)
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);
});
答案 1 :(得分:7)
您可以注册模拟并注入它而不是原始服务。
application.register('service:mockLogger', mockLogger, {
instantiate: false
});
application.inject('route', 'loggerService', 'service:mockLogger');
application.inject('controller', 'loggerService', 'service:mockLogger');
我在第三方登录验收测试中使用此方法模拟torii
库。我希望将来有一个更好的解决方案。
答案 2 :(得分:3)
现有的答案效果很好,但有一种方法可以避免重命名服务并跳过注入。
上查看https://github.com/ember-weekend/ember-weekend/blob/fb4a02353fbb033daefd258bbc032daf070d17bf/tests/acceptance/keyboard-shortcuts-test.js#L13和用法我会在这里将它作为我之前在这里的测试助手的更新,因此它是替代品,但您可能只想关注上面的链接。
// 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);
}
从https://guides.emberjs.com/v2.5.0/testing/acceptance/#toc_custom-test-helpers
执行jslint和帮助程序注册步骤我可以这样调用它,在我的例子中省略了一个重定向(window.location)服务,我们想要这样做,因为重定向中断了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');
});