【问题标题】:Angularjs Mocha test $q promises without $rootScope.$applyAngularjs Mocha 测试 $q 没有 $rootScope.$apply 的承诺
【发布时间】:2016-07-29 15:56:25
【问题描述】:

我有这项服务:

    angular.module('domeeApp')
        .factory('streamWidget', streamWidgetFactory);

    function streamWidgetFactory($q) {
        return {
            loadContent: function() { 
                return $q(function(resolve, reject) {
                    resolve('test');
                }) 
            }
        }
    }

我正在用 karma/mocha/chai 测试它:

describe('streamWidget', function() {
    beforeEach(module('domeeApp'));
    var streamWidget;
    var $timeout;

    beforeEach(inject(function(_$timeout_, _streamWidget_) {
        streamWidget = _streamWidget_;
        $timeout = _$timeout_;
    }));


    it('should load new content', function(done) {        
        streamWidget.loadContent()
        .then(function(res) {
            expect(res).to.equal('test');
            done();
        })
        .catch(function(){})
        $timeout.flush();
    });    
});

由于 $q 承诺不适用于 mocha,我正在关注 this answer,它表示要添加 $timeout.flush() 以强制执行承诺的 .then 方法。

问题是,在调用 .flush() 之后,我的所有应用都被唤醒了,我开始从 angular-mocks 中得到这个错误:

Error: Unexpected request: GET /partials/page/view/index.

我知道 $httpBackend,但如果要模拟我的应用在启动时发出的所有请求,那将是疯狂

有没有办法让 $q 承诺与 mocha 一起工作,而无需调用 $timeout.flush()$rootScope.$apply()

【问题讨论】:

  • 问题的主题是 Chai,而不是 Mocha。 stackoverflow.com/a/37374041/3731501 的可能重复项
  • 问题的主题是 $q 服务以及如何在不使用 apply/flush 的情况下强制解决 $q 承诺。 Chai 与它无关,因为我没有使用 chai-as-promised。至少,我是这么看情况的,如果你不同意,试着解释得更好。
  • 是 Chai 做断言,而不是 Mocha。那么,你可以考虑使用 chai-as-promised,这是 $q 承诺断言的方式,无需在 then 之后调用 $apply()$digest()
  • 我尝试使用 chai-as-promised,但结果相同。如果你能告诉我一个可行的解决方案,我会接受你的回答。
  • 是的,chai-as-promised 需要一些额外的设置,如上面的链接所示。幸运的是,它有 transferPromiseness 钩子来为断言的承诺提供所需的行为(即运行一个摘要来执行整个承诺链)。

标签: angularjs mocha.js angular-promise chai karma-mocha


【解决方案1】:

如图herechai-as-promised可以用来断言$q promises。 有了这个设置

chaiAsPromised.transferPromiseness = function (assertion, promise) {
  assertion.then = promise.then.bind(promise);

  if (!('$$state' in promise))
    return;

  inject(function ($rootScope) {
    if (!$rootScope.$$phase)
      $rootScope.$digest();
  });
};

摘要循环将在 Promise 断言上自动触发,执行整个 Promise 链。

在这种情况下是规范

it('...', () => {
  ...
  expect(...).to.eventually...;
  expect(...).to.eventually...;
  $rootScope.$digest();
});

可以省略$digest()调用而成为

it('...', () => {
  ...
  expect(...).to.eventually...;
  expect(...).to.eventually...;
});

请注意,$q 承诺是同步的,它们不应从 Mocha 规范返回或调用 done 回调。

【讨论】:

  • 我试过你的解决方案,但结果是一样的。当我打电话给$rootScope.$digest() 时,无论是在chaiAsPromised.transferPromiseness 还是在其他任何地方,我都会收到关于意外请求的相同错误。事实上,我从一开始就问的是如何在不使用 $digest/$apply/$flush 的情况下进行此测试。在这一点上我想没有解决方案。
  • 看来你把事情搞混了。这些都是单独的问题。 $digest() 不是灵丹妙药。您需要触发 $digest() 来执行 $q 承诺(这就是答案所涵盖的内容)。您需要调用 flush() 来执行 $timeout 超时。您需要在单元测试中模拟 $httpBackend 请求。没有必要全部模拟它们 - 只需测试单元发出的请求。这就是 Angular 测试的工作原理。
【解决方案2】:

这是我们使用的另一种策略,因为我们从不实际上需要$httpBackend,但有时(随机)请求指令使用的模板失败(即使这些模板在@987654322 中可用@):

beforeEach(function() {
  module('app', function($provide) {
    // This is using jasmine, but the idea is the same with mocha.
    // Basically just replace $httpBackend with a function that does nothing.
    $provide.constant('$httpBackend', jasmine.createSpy('$httpBackend'));
  });
});

当然,如果你真的在其他情况下使用 $httpBackend,那么这将不起作用,因为你需要它来模拟响应对象。

【讨论】:

    猜你喜欢
    • 2016-09-22
    • 2014-04-12
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多