我有一个成员服务,它主要关心的是为特定模型和模型的后代获取数据。我在服务中使用它的原因是,这种特定类型的路由使用的是不是主键的段塞,因此需要执行store.query
而不是store.find
。当我们获取这个模型时,我有一些逻辑可以查看ember存储,看看我们是否可以在进入api查询之前从那里加载它。此外,该供应商正在关注段塞变化,并在此基础上更新当前模型。
我遇到的问题是,当涉及到如何测试这样的东西时,这似乎没有什么文档。事实上,我在这里的指南中没有看到关于测试服务的部分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筛选器加载,供应商从查询加载。
我想我终于得出了一个合理的结论。让我与您分享一下我认为处理依赖商店的单元测试服务的最佳方式。
答案实际上在于我们在编写单元测试时必须做出的假设。也就是说,我们的逻辑单元之外的一切都应该被认为是正常工作的,我们的单元应该是完全独立的。
因此,对于依赖于存储的服务,最好为存储创建一个存根或mock(请参阅此问题以了解mock和存根之间的区别)。商店本身的存根非常简单。这样的东西就可以了:
store: {
find: function() {
var mockedModel = Ember.Object.create({/*empty*/});
return mockedModel;
},
query: ...
}
如果你更喜欢使用mock,你可以做以下事情(我做得很快,所以它可能不完全有效,但足以让人理解这个想法):
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;
接下来,您所要做的就是在运行测试时将服务上的存储属性(或任何调用它的内容)设置为一个新的模拟存储实例。我是这样做的:
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服务实际上只不过是一个Ember对象,如果你正在"单元测试"该服务,它应该与它的依赖关系隔离(否则它就不是unit test
)。
根据我的理解(这可能是错误的)。如果您想测试该服务,您需要用mock实现来替换存储。
//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);
});
我还认为这就是为什么很少有文档测试服务的原因。我推荐的一个关于服务的资源是芝加哥Ember Meetup 的这篇演讲