You need to change the order of execution in your tests. Asynchronous tasks with chai-as-promised need to happen before the expectation.
it('does not work', () => {
$timeout.flush();
expect(myAsyncTask()).to.eventually.become('foo');
})
it('does work', () => {
expect(myAsyncTask()).to.eventually.become('foo');
$timeout.flush();
})
You need to initiate the call to the asynchronous task before flushing the queue of asynchronous tasks.
Also, don't use $rootScope.$digest. That may have other side effects that are not desirable within your test(s).
$timeout.flush is what you're looking for.
https://docs.angularjs.org/api/ngMock/service/$timeout
To get your specific test(s) working:
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();
})
Given the comments posted:
Should it be mentioned that it is unethical to downvote 'rival' answers on the question you're answering?
Fair enough. I think the answer is misleading, given that there is no extra setup necessary to get chai-as-promised working with Angular without having to deal with the done callback. Fwiw, I'll go ahead and try to revoke said downvote and be ethical about it.
The OP has no signs of timeout in his code and doesn't state that the task is asynchronous. $rootScope.$digest() has no side effects in specs when called outside of scope digest. The reason why it is not recommended in production is because it doesn't have the safeguards that $apply has.
$rootScope.$digest is effectively the same as $rootScope.$apply (and $scope.$apply for that matter). source
$timeout.flush will flush non-$timeout based functions just as well. It is not exclusive to $timeout based functions.
Plunker to showcase how it just works™:
plunker