Ember 控制器中的单元测试异步函数
Unit testing async functions in Ember controllers
我遇到了一个非常奇怪的问题:我正在尝试进行单元测试以在我的应用程序上实现 100% 的测试覆盖率。
当然,我为我的控制器编写了一些测试,但似乎无法使用 ember-cli 在 Ember (2.4.0) 中测试任何异步内容。
我在控制器中有一个函数可以执行此操作:
readObject() {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
}.bind(this));
}
我正在编写一个应该涵盖此功能的测试。
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject();
assert.equal(cont.get('property1'), 'someValue);
});
显然,这个断言不起作用,因为 readObject() 是异步调用,但这不是问题的根源。问题是 this.store.findRecord 中的回调正在执行 - 我的控制器 已经被销毁 !所以我在那里得到 "calling set on destroyed object" 错误。
换句话说 - 即使我将我的函数包装在一个 promise 中并像这样重新格式化这两个函数:
readObject() {
return new Promise(function(resolve) {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
resolve();
}.bind(this));
}.bind(this));
}
和
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject().then(function() {
assert.equal(cont.get('property1'), 'someValue);
});
});
这是行不通的,因为在执行 readObject() 之后,我的控制器立即被销毁,而不是等待任何回调。
因此,它可以是任何异步调用而不是 store.findRecord - 例如,它可以是 Ember.run.later。
有人遇到同样的问题吗?我读了很多文章,我不敢相信 Ember 有这么大的社区却没有提供进行异步单元测试的方法。
如果有人有任何线索 - 请给我提示,因为我有点迷路了。目前我有两个想法:
我弄错了控制器,Ember 不认为其中有任何异步操作。但即使我将异步调用移至服务 - 我在为它们编写单元测试时遇到了同样的问题。
我必须将我的功能分解为
readObject() {
this.store.findRecord('myModel',1).then(this.actualReadObject.bind(this));
}
actualReadObject(obj) {
this.set('property1',obj.get('property2');
}
至少让回调主体覆盖测试,但这意味着我的应用程序从未获得 100% 的测试覆盖率。
提前感谢您提供任何线索。 :)
我遇到了类似的问题,查看 QUnit API - async 我解决了它。尝试以下操作:
// ... in your controller ...
readObject() {
return this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1', obj.get('property2');
}.bind(this));
}
// ... in your tests, either with assert.async: ...
const done = assert.async(); // asynchronous test due to promises usage
Ember.run(function(){
subject.readObject().then(function(){
// once you reach this point you are sure your promise has been resolved
done();
});
});
// or without assert.async
let promise;
Ember.run(function(){
promise = subject.readObject();
});
return promise;
在单元测试的情况下,我也会模拟其他依赖项,例如:store。
this.subject({
property1: null,
store: {
findRecord(modelName, id){
assert.equal(modelName, "myModel1");
assert.equal(id, 1);
return new Ember.RSVP.Promise(function(resolve){
resolve(Ember.Object.create({ property2: "a simple or complex mock" }));
})
}
}
});
我不确定第二种情况(没有 assert.async
的那个)。我认为它也会起作用,因为测试套件 returns 是一个承诺。这会被等待承诺的 QUnit 认可。
我在这里复制了自己的解决方案,因为注释中的代码格式不太好。
test('async', function (assert) {
const cont = this.subject();
const myModel = cont.get('store').createRecord('myModel'); //
// Make store.findRecord sync
cont.set('store',{
findRecord(){
return { then : function(callback) { callback(myModel); } }
}
});
// Sync tests
assert.equal(cont.get('property2'), undefined);
cont.readObject(); // This line calls store.findRecord underneath
assert.equal(cont.get('property2'), true);
});
所以,我只是把 store.findRecord 变成了一个同步功能,它运行完美。再一次 - 非常感谢 Pavol 提供的线索。 :)
我遇到了一个非常奇怪的问题:我正在尝试进行单元测试以在我的应用程序上实现 100% 的测试覆盖率。 当然,我为我的控制器编写了一些测试,但似乎无法使用 ember-cli 在 Ember (2.4.0) 中测试任何异步内容。
我在控制器中有一个函数可以执行此操作:
readObject() { this.store.findRecord('myModel',1).then(function(obj) { this.set('property1',obj.get('property2'); }.bind(this)); }
我正在编写一个应该涵盖此功能的测试。
test('action readObject', function (assert) { const cont = this.subject(); cont.readObject(); assert.equal(cont.get('property1'), 'someValue); });
显然,这个断言不起作用,因为 readObject() 是异步调用,但这不是问题的根源。问题是 this.store.findRecord 中的回调正在执行 - 我的控制器 已经被销毁 !所以我在那里得到 "calling set on destroyed object" 错误。
换句话说 - 即使我将我的函数包装在一个 promise 中并像这样重新格式化这两个函数:
readObject() {
return new Promise(function(resolve) {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
resolve();
}.bind(this));
}.bind(this));
}
和
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject().then(function() {
assert.equal(cont.get('property1'), 'someValue);
});
});
这是行不通的,因为在执行 readObject() 之后,我的控制器立即被销毁,而不是等待任何回调。 因此,它可以是任何异步调用而不是 store.findRecord - 例如,它可以是 Ember.run.later。
有人遇到同样的问题吗?我读了很多文章,我不敢相信 Ember 有这么大的社区却没有提供进行异步单元测试的方法。
如果有人有任何线索 - 请给我提示,因为我有点迷路了。目前我有两个想法:
我弄错了控制器,Ember 不认为其中有任何异步操作。但即使我将异步调用移至服务 - 我在为它们编写单元测试时遇到了同样的问题。
我必须将我的功能分解为
readObject() { this.store.findRecord('myModel',1).then(this.actualReadObject.bind(this)); } actualReadObject(obj) { this.set('property1',obj.get('property2'); }
至少让回调主体覆盖测试,但这意味着我的应用程序从未获得 100% 的测试覆盖率。
提前感谢您提供任何线索。 :)
我遇到了类似的问题,查看 QUnit API - async 我解决了它。尝试以下操作:
// ... in your controller ...
readObject() {
return this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1', obj.get('property2');
}.bind(this));
}
// ... in your tests, either with assert.async: ...
const done = assert.async(); // asynchronous test due to promises usage
Ember.run(function(){
subject.readObject().then(function(){
// once you reach this point you are sure your promise has been resolved
done();
});
});
// or without assert.async
let promise;
Ember.run(function(){
promise = subject.readObject();
});
return promise;
在单元测试的情况下,我也会模拟其他依赖项,例如:store。
this.subject({
property1: null,
store: {
findRecord(modelName, id){
assert.equal(modelName, "myModel1");
assert.equal(id, 1);
return new Ember.RSVP.Promise(function(resolve){
resolve(Ember.Object.create({ property2: "a simple or complex mock" }));
})
}
}
});
我不确定第二种情况(没有 assert.async
的那个)。我认为它也会起作用,因为测试套件 returns 是一个承诺。这会被等待承诺的 QUnit 认可。
我在这里复制了自己的解决方案,因为注释中的代码格式不太好。
test('async', function (assert) {
const cont = this.subject();
const myModel = cont.get('store').createRecord('myModel'); //
// Make store.findRecord sync
cont.set('store',{
findRecord(){
return { then : function(callback) { callback(myModel); } }
}
});
// Sync tests
assert.equal(cont.get('property2'), undefined);
cont.readObject(); // This line calls store.findRecord underneath
assert.equal(cont.get('property2'), true);
});
所以,我只是把 store.findRecord 变成了一个同步功能,它运行完美。再一次 - 非常感谢 Pavol 提供的线索。 :)