If you have ever wondered how to test promises in Angular this post is for you.
Assume we have a function which in turn invokes two other functions. Here
submitReport will invoke two services. Our
report will be persisted via
reportsGateway and, if successful, the
router will navigate the user to a list of reports.
A developer not used to testing asynchronous code might write a unit test as follows
Running the test leads to
We can add a log statement into the anonymous
then function and another one above our assertions. When running the tests we witness
We have a timing issue. The assertion happens before the promise is resolved.
The quick and dirty solution is to add a timeout to the test after calling
submitReport. It gives the promise enough time to resolve before checking the assertions.
Run the test and its passing. Sweet. Let’s continue with the next feature…
Change the assertion on the
router to state the opposite. Look at the added
Run it. The tests are still passing. How?
Because our test finishes without ever running the anonymous function in
setTimeout. Thus the test has 0 assertions.
Our test is back to green and fails as soon as we try to swap
Sadly, by going for the timeout solution, we have made our test unnecessarily slow. The test will wait these 10ms even if less would be fine too.
We can give the test exactly the amount it needs by using the Promise already returned by
One issue I have with the above is the fact that
submitReport has to return a
Promise for it to work. What if we do not care about the return value? Additionally it intermingles the Act and Assert part in Arrange Act Assert and adds another level of indentation which decreases the readability.
Thankfully Angular offers fakeAsync to test code as if it were synchronous. The promise is fulfilled immediately after you call
The test is readable, not flaky, not wasting time and passes. Great.