chai-as-promise测试不适用于$ q承诺

时间:2016-04-18 22:00:15

标签: angularjs karma-runner chai chai-as-promised

我正试图让chai-as-promised与karma单元测试一起使用$q承诺。

  svc.test = function(foo){
    if (!foo){
      // return Promise.reject(new Error('foo is required'));
      return $q.reject(new Error('foo is required'));
    } else {
      // get data via ajax here
      return $q.resolve({});
    }
  };


  it.only('should error on no foo', function(){
    var resolvedValue = MyServices.test();
    $rootScope.$apply();
    return resolvedValue.should.eventually.be.rejectedWith(TypeError, 'foo is required');
  });

单元测试只是超时了。我不确定我在这里做错了什么来得到正确解决的承诺。使用$q似乎是一个问题 - 当我使用原生Promise.reject()时,它运行正常。

我在这里提交了一张票,但似乎没有人回应: https://github.com/domenic/chai-as-promised/issues/150

2 个答案:

答案 0 :(得分:2)

template<class T> inline std::vector<std::shared_ptr<T>> * getVector() const { auto key = getKey<T>(); return reinterpret_cast<std::vector<std::shared_ptr<T>> *>( (_componentMap.count(key) ? _componentMap.at(key).get() : const_cast<std::vector<std::shared_ptr<Component>> *>(&_emptyComponentSharedPtrVec)) ); } 期望修改承诺断言的方式是transferPromiseness method

  

默认情况下,Chai作为Promised的断言返回的承诺是   常规的Chai断言对象,用单个then方法扩展   源自输入承诺。例如,要更改此行为   用更有用的糖方法输出一个承诺,例如找到的   在大多数promise库中,您可以覆盖   chaiAsPromised.transferPromiseness。

对于Angular 1.3+支持,chai-as-promised承诺可以由$q属性进行鸭子类型化,因此本机承诺不会受到影响:

$$state

chaiAsPromised.transferPromiseness = function (assertion, promise) { assertion.then = promise.then.bind(promise); if (!('$$state' in promise)) return; inject(function ($rootScope) { if (!$rootScope.$$phase) $rootScope.$digest(); }); }; 个链都声明了chaiAsPromised的承诺。即使承诺已经解决,链的其余部分仍然需要使用then手动触发摘要。

只要规范不包含异步代码,它就会变成同步,不需要返回promise:

$rootScope.$digest()

在未设置it('...', () => { ... expect(...).to.eventually...; expect(...).to.eventually...; }); 的每组$rootScope.$digest()断言/期望之后,等于强制eventually

transferPromiseness

答案 1 :(得分:1)

您需要更改测试中的执行顺序。具有chai-as-promise的异步任务需要在期望之前发生

it('does not work', () => {
  $timeout.flush();
  expect(myAsyncTask()).to.eventually.become('foo');
})

it('does work', () => {
  expect(myAsyncTask()).to.eventually.become('foo');
  $timeout.flush();      
})

刷新异步任务队列之前,需要启动对异步任务的调用。

另外,请勿使用$rootScope.$digest可能还有其他副作用,这些副作用在您的测试中是不可取的。

$timeout.flush正是您正在寻找的。

https://docs.angularjs.org/api/ngMock/service/ $超时

让您的特定测试工作:

it('should error on no foo', function(){
  MyServices.test().should.eventually.be.rejectedWith(TypeError, 'foo is required')
  $rootScope.$apply();
});

it('should pass on foo', function(){
  MyServices.test('foo').should.eventually.become({});
  $rootScope.$apply();      
}

tl; dr

it('async test', () => {
  setup();
  expect();
  execute();
})

it('sync test', () => {
  setup();
  execute();
  expect();
})

鉴于发布的评论:

  

是否应该提到在你回答的问题上对“竞争对手”的答案进行投票是不道德的?

足够公平。我认为答案是误导性的,因为没有必要进行额外的设置,以便在不必处理done回调的情况下使用Angular进行chai-as-promise。 Fwiw,我会继续尝试撤销所说的downvote并对此有道德。

  

OP在代码中没有超时的迹象,并且没有说明任务是异步的。在范围摘要之外调用时,$rootScope.$digest()在规范中没有副作用。不建议在生产中使用它的原因是因为它没有$apply所具有的安全措施。

$rootScope.$digest实际上与$rootScope.$apply(和$scope.$apply相同)。 source

$timeout.flush也会刷新非基于$timeout的函数。它不是基于$timeout的函数所独有的。

Plunker展示 just works™plunker