测试AngularJS在Jasmine 2.0中的承诺

时间:2014-04-17 11:24:26

标签: javascript angularjs jasmine

我一直试图围绕Jasmine 2.0和AngularJS承诺。我知道:

如何在Jasmine 2.0中使用新的异步语法测试AngularJS承诺?

3 个答案:

答案 0 :(得分:42)

致电promise.resolve()后:

  • 致电$timeout.flush()。这将强制摘要周期并传播承诺解决方案
  • 致电done()。这告诉Jasmine异步测试已经完成

以下是一个示例(Demo on Plunker):

describe('AngularJS promises and Jasmine 2.0', function() {
    var $q, $timeout;

    beforeEach(inject(function(_$q_, _$timeout_) {
        // Set `$q` and `$timeout` before tests run
        $q = _$q_;
        $timeout = _$timeout_;
    }));

    // Putting `done` as argument allows async testing
    it('Demonstrates asynchronous testing', function(done) {
        var deferred = $q.defer();

        $timeout(function() {
            deferred.resolve('I told you I would come!');
        }, 1000); // This won't actually wait for 1 second.
                  // `$timeout.flush()` will force it to execute.

        deferred.promise.then(function(value) {
            // Tests set within `then` function of promise
            expect(value).toBe('I told you I would come!');
        })
        // IMPORTANT: `done` must be called after promise is resolved
        .finally(done);

        $timeout.flush(); // Force digest cycle to resolve promises
    });
});

答案 1 :(得分:4)

对我而言,$timeout.flush()效果不佳,但我的规格中有多个异步调用。我找到了$rootScope.$apply(),作为在每次异步调用时强制digest的方法。

describe('AngularJS promises and Jasmine 2.0', function () {
  beforeEach(inject(function (_$q_, _$timeout_, _$rootScope_) {
    $q = _$q_
    $timeout = _$timeout_
    $rootScope = _$rootScope_
  }))

  it('demonstrates asynchronous testing', function (done) {
    var defer = $q.defer()

    Async.call()
    .then(function (response) {
      // Do something

      var d = $q.defer()
      Async.call()
      .then(function (response) {
        d.resolve(response)
        $rootScope.$apply() // Call the first digest 
      })
      return d.promise
    })
    .then(function (response) {
      // Do something after the first digest

      Async.call()
      .then(function (response) {
        defer.resolve(response) // The original defer
        $rootScope.$apply() // Call the second digest
      })
    })

    defer.promise.then(function(value) {
      // Do something after the second digest
      expect(value).toBe('I told you I would come!')
    })
    .finally(done)

    if($timeout.verifyNoPendingTasks())
      $timeout.flush() 
  })
})

它就像一个链式异步调用的东西。希望它有助于对话。 此致

答案 2 :(得分:3)

这个答案不会给上面的内容添加任何新内容,它只是为了更详细地表达答案,因为它对我有用。当我发生上述问题中描述的问题时,我花了很多时间尝试找到一种方法来确保所有承诺都有时间完成并且所有断言都被断言。

在我的情况下,我有一系列承诺,在每个承诺之后,我需要确保结果符合我的期望。我没有使用deferred创建任何承诺,而是调用现有的承诺。

所以,问题是$timeout.flush()对我来说已经足够了。我的工作测试看起来像这样:

describe("Plain command without side-effects", function() {
    it("All usecases", inject(function($timeout) {
        console.log("All together");
        expect(state.number).toEqual(1);
        cmdHistory
            .execute(increaseState, decreaseState)
            .then(function() {
                console.log("Execute works");
                expect(state.number).toEqual(2);
                return cmdHistory.redo(); // can't redo, nothing's undone
            })
            .then(function() {
                console.log("Redo would not work");
                expect(state.number).toEqual(2);
                return cmdHistory.undo();
            })
            .then(function() {
                console.log("Undo undoes");
                expect(state.number).toEqual(1);
                return cmdHistory.undo();
            })
            .then(function() {
                console.log("Next undo does nothing");
                expect(state.number).toEqual(1);
                return cmdHistory.redo(); // but still able to redo

            })
            .then(function() {
                console.log("And redo redoes neatly");
                expect(state.number).toEqual(2);
            });

        $timeout.flush();
    }));

此测试专用于确保commandHistory对象正常工作,它必须采取行动:executeunExecute,以及三种方法:executeundo,{ {1}},所有这些都会返回承诺。

如果没有redo,我在日志输出中的所有内容都是$timeout.flush(),并且没有其他日志消息。添加All together已经解决了所有问题,现在我显示了所有消息并执行了所有断言

<强>更新 还有另外一个选择:您可以编写测试套件而不用$timeout.flush()链接承诺,但只需在调用每个承诺后进行刷新,以确保完成:

then

在某些情况下请注意这一事实,当 it("All usecases 2", inject(function($timeout) { console.log("All usecases 2"); expect(state.number).toEqual(1); console.log("Execute works"); cmdHistory.execute(increaseState, decreaseState); $timeout.flush(); expect(state.number).toEqual(2); console.log("Redo would not work"); cmdHistory.redo(); // can't redo, nothing's undone $timeout.verifyNoPendingTasks(); expect(state.number).toEqual(2); console.log("Undo undoes"); cmdHistory.undo(); $timeout.flush(); expect(state.number).toEqual(1); console.log("Next undo does nothing"); cmdHistory.undo(); $timeout.verifyNoPendingTasks(); expect(state.number).toEqual(1); console.log("And redo redoes neatly"); cmdHistory.redo(); // but still able to redo $timeout.flush(); expect(state.number).toEqual(2); })); undo等方法未返回承诺时,我会调用redo而不是$timeout.verifyNoPendingTasks()。如果它的好坏,这很难说。

然而在这种情况下,测试看起来更合理,更简单。