Ember 控制器中的单元测试异步函数

Unit testing async functions in Ember controllers

我遇到了一个非常奇怪的问题:我正在尝试进行单元测试以在我的应用程序上实现 100% 的测试覆盖率。 当然,我为我的控制器编写了一些测试,但似乎无法使用 ember-cli 在 Ember (2.4.0) 中测试任何异步内容。

  1. 我在控制器中有一个函数可以执行此操作:

    readObject() {
       this.store.findRecord('myModel',1).then(function(obj) {
          this.set('property1',obj.get('property2');
       }.bind(this));
    }
    
  2. 我正在编写一个应该涵盖此功能的测试。

    test('action readObject', function (assert) {
       const cont = this.subject();
       cont.readObject();
       assert.equal(cont.get('property1'), 'someValue);
    });
    
  3. 显然,这个断言不起作用,因为 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 有这么大的社区却没有提供进行异步单元测试的方法。

如果有人有任何线索 - 请给我提示,因为我有点迷路了。目前我有两个想法:

  1. 我弄错了控制器,Ember 不认为其中有任何异步操作。但即使我将异步调用移至服务 - 我在为它们编写单元测试时遇到了同样的问题。

  2. 我必须将我的功能分解为

    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 提供的线索。 :)