Unit testing 单元测试获取数据的Ember服务

Unit testing 单元测试获取数据的Ember服务,unit-testing,ember.js,ember-data,ember-cli,ember-qunit,Unit Testing,Ember.js,Ember Data,Ember Cli,Ember Qunit,我有一个ember服务,它主要关注的是获取特定模型及其后代的数据。我在服务中使用此选项的原因是,此特定类型的路由使用的slug不是主键,因此需要执行存储。查询而不是存储。查找。当我们获取这个模型时,我有一些逻辑可以窥视ember存储,看看我们是否可以在转到api查询之前从那里加载它。此外,该供应商正在关注slug的变化,并在此基础上更新当前模型 我遇到的问题是,当涉及到如何测试这样的东西时,这似乎只有很少的文档。事实上,我在这里的指南中没有看到关于测试服务的部分 这是有关服务的一个片段 impo

我有一个ember服务,它主要关注的是获取特定模型及其后代的数据。我在服务中使用此选项的原因是,此特定类型的路由使用的slug不是主键,因此需要执行
存储。查询
而不是
存储。查找
。当我们获取这个模型时,我有一些逻辑可以窥视ember存储,看看我们是否可以在转到api查询之前从那里加载它。此外,该供应商正在关注slug的变化,并在此基础上更新当前模型

我遇到的问题是,当涉及到如何测试这样的东西时,这似乎只有很少的文档。事实上,我在这里的指南中没有看到关于测试服务的部分

这是有关服务的一个片段

import Ember from 'ember';

export default Ember.Service.extend({
    _vendorSlug: null,
    vendor: null,

    vendorSlug: function (key, value) {
        if (arguments.length > 1) {
            if (this._vendorSlug) {
                return this._vendorSlug;
            }

            this._vendorSlug = value;
        }

        return this._vendorSlug;
    }.property(),

    ensureVendorLoaded: function (slug) {
        var service = this,
            vendorSlug = slug || service.get('vendorSlug'),
            currentVendor = service.get('vendor'),
            storedVendor;

        if (!Ember.isNone(currentVendor) && (vendorSlug === currentVendor.get('slug'))) {
            return new Ember.RSVP.Promise((resolve) => {
                        resolve(currentVendor);
                    });
        } else {
            var storedVendors = service.store.peekAll('vendor').filter((vendor) => { 
                return vendor.get('slug') === vendorSlug;
            });

            if (storedVendors.length) {
                storedVendor = storedVendors[0];
            }
        }

        if (!Ember.isNone(storedVendor)) {
            service.set('vendorSlug', storedVendor.get('slug'));

            return new Ember.RSVP.Promise((resolve) => {
                    resolve(storedVendor);
                });
        }

        return service.store.queryRecord('vendor', {slug: vendorSlug}).then((vendor) => {
            service.set('vendor', vendor);
            service.set('vendorSlug', vendor.get('slug'));

            return vendor;
        });
    },

    _vendorSlugChanged: function () {
        if (this.get("vendorSlug") === this.get("vendor.slug")) {
            return;
        }

        this.ensureVendorLoaded();
    }.observes('vendorSlug')
});

我希望能够在这里断言商店交互的几个场景。供应商已设置,供应商已从peek筛选器加载,供应商已从查询加载。

我不确定这是否是您想要的答案,但我还是会尝试一下。Ember服务实际上只不过是一个Ember对象,如果您正在“单元测试”该服务,它应该与其依赖项隔离(否则它就不是一个
单元测试

根据我的理解(这可能是错误的)。如果要测试该服务,需要用模拟实现替换存储

//tests/unit/services/my-service.js
test('some scenario', function(assert) {
   let service = this.subject({
     store: Ember.Object.create({
        peekAll(modelName){
          //Return array for this scenario
        },
        query(model, params){
          //Return array for this scenario
        }
     });
   });
   assert.ok(service);
});
我还认为这就是为什么几乎没有文档测试服务的原因。
关于服务,我推荐的一个资源是这次讲座

我想我终于得出了一个合理的结论。让我和大家分享一下我认为最好的方法是使用依赖于商店的单元测试服务

答案确实在于我们在写作时必须做出的假设。也就是说,我们的逻辑单元之外的一切都应该被认为是正常工作的,我们的单元应该是完全独立的

因此,对于依赖于存储的服务,最好为存储创建存根或模拟(请参阅以了解模拟和存根之间的区别)。商店本身的存根非常简单。这样做可以:

 store: {
    find: function() {
       var mockedModel = Ember.Object.create({/*empty*/});
       return mockedModel;
    },
    query: ...
 }
如果您更喜欢使用模拟,您可以执行以下操作(我将此操作做得非常快,因此它可能无法完全工作,但足以让人理解此想法):

接下来,您所要做的就是在运行测试时将服务上的store属性(或您调用它的任何内容)设置为新的mock store实例。我是这样做的:

import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';
import MockStore from '../../helpers/mock-store';

let session;
let store;

const username = '';
const password = '';

moduleFor('service:authentication', 'Unit | Service | authentication', {
    beforeEach() {
        session = Ember.Object.create({});
        store = new MockStore();
    }
});

test('it should authenticate the user', function (assert) {
    // this sets the store property of the service to the mock store
    let authService = this.subject({session: session, store: store});

    authService.authenticate(username, password).then(() => {
        assert.ok(session.get('username'));
    });
});

关于测试这些情况的文档肯定很差,所以也许有更好的方法,但这就是我现在要介绍的。此外,如果您查看使用ember的项目,它们遵循与我在这里描述的模式相似的模式,但方式更高级。

我也在尝试这样做,但找不到任何关于该主题的文档。我已经开始研究一些使用ember的项目,我希望很快能找到一些例子。如果有帮助的话。@cojomojo您注释掉的
MockStore
模块看起来会是什么样子?请看我的答案,以获取一个示例。是否有一种方法可以在设置方法或其他方法中实现这一点?我将向您指出@comojo在下面留下的答案。他将存储作为一个外部文件,并在测试中导入它(并在beforeach中实例化存储),我相信可以接受一个带有这些内容文档的PR。
import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';
import MockStore from '../../helpers/mock-store';

let session;
let store;

const username = '';
const password = '';

moduleFor('service:authentication', 'Unit | Service | authentication', {
    beforeEach() {
        session = Ember.Object.create({});
        store = new MockStore();
    }
});

test('it should authenticate the user', function (assert) {
    // this sets the store property of the service to the mock store
    let authService = this.subject({session: session, store: store});

    authService.authenticate(username, password).then(() => {
        assert.ok(session.get('username'));
    });
});