gpt4 book ai didi

ember.js - 如何在验收测试中模拟 Ember-CLI 服务?

转载 作者:行者123 更新时间:2023-12-03 08:58:16 28 4
gpt4 key购买 nike

快速总结/tldr:

  • 如果可以使用 resolver 解析原始服务,Ember 的容器查找过程 + Ember-CLI 的模块解析器似乎不允许手动取消注册服务然后注册替换。 (我想做described here的方法,但是不行)
  • 如何在验收测试中模拟 Ember-CLI 服务而不使用 hacky 的自定义解析器? ( example project/acceptance test here )

  • 详解+实例

    创建一个注入(inject) Controller 的新服务:
    ember generate service logger

    服务/logger.js
    export default Ember.Object.extend({
    log: function(message){
    console.log(message);
    }
    });

    初始化程序/logger-service.js
    export function initialize(container, application) {
    application.inject('route', 'loggerService', 'service:logger');
    application.inject('controller', 'loggerService', 'service:logger');
    }

    该服务通过其注入(inject)名称 loggerService 访问。 ,在应用程序 Controller 上的 Action 处理程序中:

    在 Controller 中使用服务

    模板/应用程序.hbs
    <button id='do-something-button' {{action 'doSomething'}}>Do Something</button>

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

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

    我创建了一个验收测试来检查按钮单击是否触发了服务。目的是模拟服务并确定它是否在没有实际触发服务实现的情况下被调用——这避免了真实服务的副作用。
    ember generate acceptance-test application

    测试/验收/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');
    });
    });

    这是基于谈话 Testing Ember Apps: Managing Dependency通过 mixonic 建议取消注册现有服务,然后重新注册模拟版本:
    application.__container__.unregister('service:logger');
    application.register('service:logger', mockLogger, {instantiate: false});

    不幸的是, 这不起作用使用 Ember-CLI。罪魁祸首是 this line在 Ember 的容器中:
    function resolve(container, normalizedName) {
    // ...
    var resolved = container.resolver(normalizedName) || container.registry[normalizedName];
    // ...
    }

    这是容器查找链的一部分。问题是容器的 resolve方法检查 resolver在检查其内部 registry 之前. application.register命令将我们的模拟服务注册到容器的 registry , 但是当 resolve被称为 resolver 的容器检查在它查询 registry 之前. Ember-CLI 使用自定义 resolver将查找匹配到模块,这意味着它将始终解析原始模块而不使用新注册的模拟服务。解决方法看起来很糟糕,涉及修改 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
    });

    这似乎不是必需的,并且与上述幻灯片中建议的服务模拟不匹配。有没有更好的方法来模拟这项服务?

    本题使用的 ember-cli 项目位于 this example project on github.

    最佳答案

    解决方案的简短版本:您注册的模拟服务必须具有与您尝试模拟的“真实”服务不同的 service:name 。

    验收测试:

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

    关于ember.js - 如何在验收测试中模拟 Ember-CLI 服务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28422375/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com