延迟测试值,直到承诺得到解决

时间:2017-06-08 05:12:22

标签: angularjs unit-testing promise mocha sinon

true内解决了承诺后,我正在尝试测试值是否已更改为$onInit。我尽可能地关注example in this Stack Overflow question/answer。这是我的代码:

class TestCtrl {
  constructor(SearchService) {
    this.testValue = false;
    this.SearchService = SearchService;
  }
  $onInit() {
    this.SearchService.getResults()
      .then(function () {
        this.testValue = true;
      });
  }
}
TestCtrl.$inject = ['SearchService'];

这是我试图运行的测试(使用mocha,chai,sinon):

it('should work', function() {
  ctrl = $componentController('test', {
    SearchService: SearchService
  }, {});
  sinon.stub(SearchService, 'getResults').resolves({response:{data: 'data'}});
  ctrl.$onInit();
  $rootScope.$apply();
  ctrl.testValue.should.equal(true);
});

我应该在ctrl.testValue内测试then吗?另外,使用this example是一个坏主意,因为该示例不使用带有$onInit生命周期钩子的组件?

据我所读,no,“在测试中不要使用expect。”但根据我的read elsewhere,我不太确定。

如果我遗漏了一些关于如何测试承诺(可能是存根不是要走的路?)和/或如何测试$onInit生命周期中发生的事情,我不会感到惊讶钩。

如果问题需要更多细节,请询问,我会尽力添加它们。

3 个答案:

答案 0 :(得分:2)

修改:结帐$onInit方法:

$onInit() {
    this.SearchService.getResults() 
      .then(function () {
         // `this` in anonymous function is reffering to window not the controller instance
        this.testValue = true;
      });
  }

$onInit() {
    var self = this;
    self.SearchService.getResults() 
      .then(function () {
        self.testValue = true;
      });
  }

您的示例是正确的

这是在angularjs中测试异步代码的方法 - 它像同步代码一样进行测试。存根'执行$ rootScope时会返回返回的promise。$ apply()。

为什么它不起作用

stub.resolves()返回的承诺不是一个有希望的承诺。无法使用$rootScope触发解决,因为它不是角度世界的一部分。它的承诺解析队列与其他东西联系在一起,因此需要像通常测试异步代码一样进行测试。

Angular并不依赖于JavaScript的本机Promise实现 - 它使用了Q&#39}承诺库的轻量级实现,该库包含在名为$q的服务中

您引用的answer使用相同的服务来创建并从存根中返回承诺

为了让代码工作 - 像测试同步代码一样进行测试 - 你应该返回一个$q承诺(通过在$q.when(value)中包装一个值),调用$rootScope.$apply()将执行then块中的代码,然后继续$rootScope.$apply()行下面的代码。

以下是一个例子:

it('Sinon should work with angular promises', function () {

    var resolved = 'resolved';
    var promise = $q.when(resolved);
    // Our async function
    var stub = sinon.stub().returns(promise);
    // Callback to be executed after the promise resolves
    var handler = sinon.stub(); 

    stub().then(handler); // async code

    // The handler will be called only after $rootScope.$apply()
    handler.callCount.should.equal(0);

    // triggers digest which will resolve `ready` promises 
    // like those created with $q.when(), $q.resolve() or those created 
    // using the $q.defer() and deferred.resolve() was called
    // this will execute the code inside the appropriate callback for
    // `then/catch/finally` for all promises and then continue 
    // with the code bellow -> this is why the test is considered `synchronous`
    $rootScope.$apply();

    // Verify the handler was called and with the expected value
    handler.callCount.should.equal(1);
    handler.should.have.been.calledWith(resolved);

})

此处处于行动中test promise synchronously in angular

答案 1 :(得分:1)

首先,您应该阅读how Mocha expects you to test async code

从快速位开始:

  1. 你走在正确的道路上 - 只有一些地方缺失。
  2. 是的,您应该在then
  3. 内进行测试
  4. 您链接的示例很好。只是理解它。
  5. 绝对没有理由避免在then内断言测试。实际上,它通常是断言承诺的已解决价值的唯一方法。
  6. 您的测试代码的主要问题是它尝试在结果可用之前断言(因为承诺在稍后的时间点解析,它们是asynchronous)。

    您尝试测试的代码的主要问题是无法知道init函数何时已解决。

    我们可以通过等待存根SearchService.getResults来解决(因为我们在测试中控制存根)来处理#2,但这假设对onInit的实现有太多的了解,所以是一个糟糕的黑客。

    相反,我们只需在TestCtrl中返回承诺,就可以修复onInit中的代码:

    //main code / TestCtrl
    $onInit() {
      return this.SearchService.getResults()
        .then(function () {
          this.testValue = true;
        }); 
    }
    

    现在我们可以在测试执行期间发生的事情之前等待对onInit的任何调用解决!

    要修复测试,我们首先在包装测试函数中添加一个参数。 Mocha会看到这个并传递一个你可以在测试结束时调用的函数。

    it('should work', function(done) {
    

    这使它成为异步测试。现在让我们修复测试部分:

    ctrl.$onInit().then( () => {
      ctrl.testValue.should.equal(true);
      done(); // signals to mocha that the test is finished ok
    }).catch(done); // pass any errors to the callback
    

    您可能还会发现this answer enlightening(如果它可以帮助您,则可以提供支持)。阅读之后你也可以理解为什么Mocha也支持通过从测试中返回一个promise而放弃done回调。进行更短的测试:

    return ctrl.$onInit().then( () => {
      ctrl.testValue.should.equal(true);
    });
    

答案 2 :(得分:-1)

const ref = firebase.database().ref('order/'); ref.on('child_added', snapshot => { // snapshot now contains the data of -KmDWQ... snapshot.child("items").forEach((itemSnapshot) => { console.log(itemSnapshot.key, itemSnapshot.val().name); }); }); 未返回承诺。使用sinon.stub(SearchService, 'getResults').resolves({response:{data: 'data'}});。 我建议这样做:

$q

您无需在当时测试ctrl = $componentController('test', { SearchService: SearchService }, {}); let deferred =$q.defer(); deferred.resolve({response:{data: 'data'}}); sinon.stub(SearchService, 'getResults').resolves(deferred.promise); ctrl.$onInit(); $rootScope.$apply(); ctrl.testValue.should.equal(true); 。一般来说,我建议不要在你的规范中ctrl.testValue内断言。如果承诺永远不会得到解决,规格将不会失败。这实际上可以给你一种虚假的安全感,你的测试没有做任何事情。但那只是我的个人意见。 一旦存根返回一个promise,你的测试就会通过。理想情况下,如果服务正在进行http调用,我建议使用$ httpBackend。 欢呼声。