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

标签 unit-testing ember.js ember-data ember-cli ember-qunit

我有一个 ember 服务,主要关注的是获取特定模型和模型后代的数据。我在服务中使用它的原因是因为这个特定类型的路由使用了一个不是主键的 slug,因此需要做一个 store.query而不是 store.find .当我们获取这个模型时,我有一些逻辑可以查看 ember 存储,看看我们是否可以在进入 api 查询之前从那里加载它。此外,该供应商正在关注 slug 更改并基于此更新当前模型。

我遇到的问题是,关于如何测试这样的事情,这似乎几乎没有文档。事实上,我在这里的指南中的任何地方都没有看到有关测试服务的部分 http://guides.emberjs.com/v2.1.0/

这是相关服务的片段。

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 过滤器加载,供应商从查询加载。

最佳答案

我想我终于得出了一个合理的结论。让我与您分享我认为可能是处理依赖于商店的单元测试服务的最佳方式。

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

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

 store: {
    find: function() {
       var mockedModel = Ember.Object.create({/*empty*/});
       return mockedModel;
    },
    query: ...
 }

如果您更喜欢使用模拟,您可以执行以下操作(我做得非常快,因此它可能无法完全运行,但足以让您了解这个想法):
import Ember from 'ember';

class MockStore {
    constructor() {
        this.models = Ember.A([]);
    }

    createRecord(modelName, record) {
        // add a save method to the record
        record.save = () => {
            return new Ember.RSVP.Promise((resolve) => {
                resolve(true);
            });
        };

        if (!this.models[modelName]) {
            this.models[modelName] = Ember.A([]);
        }

        this.models[modelName].pushObject(record);

        return record;
    }

    query(modelName, query) {
        let self = this;

        return new Ember.RSVP.Promise((resolve) => {
            let model = self.models[modelName];
            // find the models that match the query
            let results = model.filter((item) => {
                let result = true;

                for (let prop in query) {
                    if (query.hasOwnProperty(prop)) {
                        if (!item[prop]) {
                            result = false;
                        }
                        else if (query[prop] !== item[prop]) {
                            result = false;
                        }
                    }
                }

                return result;
            });

            resolve(results);
        });
    }
}

export default MockStore;

接下来,您要做的就是在运行测试时将服务上的 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'));
    });
});

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

关于unit-testing - 单元测试获取数据的 Ember 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33418203/

相关文章:

ember.js - 如何使用 DS.Store.registerAdapter

php - 单元测试调用具体子方法的抽象类方法

c# - 如何在 CRM 2011 开发中编写单元测试

facebook - ember-cli 的守望者错误

Ember.js 单击时绑定(bind)类更改

javascript - 使用 Mixins 访问 Ember Controller

javascript - 为什么在 karma 和 jasmine 中运行测试时 fs.readFile 不是一个函数?

javascript - 单元测试 queryParams 订阅 (Angular5)

javascript - Ember.js:访问渲染模板中的 ApplicationController 方法

ember.js - ember-data 观察记录数组 isLoading/isUpdating/hasUpdated?