【问题标题】:Testing AngularJS promises in Jasmine 2.0在 Jasmine 2.0 中测试 AngularJS 承诺
【发布时间】:2014-04-17 11:24:25
【问题描述】:

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

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

【问题讨论】:

  • 如果测试 Promise,为什么在新旧 Jasmine 中都需要使用异步语法?您可以发布您正在尝试测试的功能吗?在很多情况下,您可以在测试中使用$timeout.flush() 和/或myPromise.resolve() 来强制它们同步。
  • @MichalCharemza 令我惊讶的是,$timeout.flush() 在不使用 Jasmine 的 done 的情况下效果很好;当我需要done + $timeout.flush() 时,我没有演示函数。如果测试使用例如一个实际的$http 后端?模拟$http 的速度(显然)更好,但是没有done$timeout.flush() 会在那里工作吗?承诺的延迟解决有什么不同吗?
  • 如果测试使用实际的$http 后端,连接到真实服务器,那么测试将是异步的,您必须使用done$timeout.flush()(或$httpBackend.flush())会影响本地运行的代码:您不能调用函数来要求服务器立即响应请求!如果您不确定如何测试特定功能(例如,使用$timeout$http),那么您可以在问题中发布该功能)。

标签: javascript angularjs jasmine


【解决方案1】:

在您致电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
    });
});

【讨论】:

  • 只是想添加一个链接到 K. Scott Allen 关于您在此处使用的 beforeEach 注入技术的帖子:odetocode.com/blogs/scott/archive/2014/05/15/…
  • 赞成,说真的,只添加 $timeout.flush() 修复了我的规格。真的太糟糕了,这没有很好的记录......
  • 上面不需要完成,所有工作都由$timeout.flush()完成
  • 有什么理由使用$timeout.flush() 而不是$rootScope.apply()?这就是官方文档使用的:docs.angularjs.org/api/ng/service/$q#testing
  • 根据Pawel Kozlowski's reply on GitHub,使用$timeout.$flush() 也会清除计时器。大多数时候$rootScope.$apply() 工作得很好,但$timeout.$flush() 涵盖更多用例
【解决方案2】:

对我来说,$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() 
  })
})

这就像一个链式异步调用的东西。希望对谈话有所帮助。 问候

【讨论】:

    【解决方案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,以及三个方法:executeundoredo,所有这些都返回承诺.

    没有$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);
        }));
    

    请注意,在某些情况下,当我的方法如undoredo 不返回promise 时,我调用$timeout.verifyNoPendingTasks() 而不是flush。这很难说是好是坏。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 2014-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-18
      相关资源
      最近更新 更多