In an Angular 7 unit test, is there a way to avoid the double async( async(){} )
syntax when combining async support along with the async..await
keywords?
I'm new to angular but am an experienced programmer, and I'm having trouble landing on my preferred test style.
I would like to safely use async..await
in tests, and I understand the below syntax. However, when instructing devs new to modern javascript and/or the concept of async..await
the double async(async())
syntax is redundant and confusing to them. They are leaving out the outer async. Exceptions thrown in the service are causing failures to be reported outside of the actual test which is difficult to track down.
It seems like one of the following would be better:
it()
should magically support async..await
and wrap my callback in async()
so that I don't have to think about it.it()
should take an optional function parameter (i.e., async
or fakeAsync
) that will wrap my callback.it()
variations ita()
and itfa()
should exist that will wrap my callback with the appropriate async helper.it()
wraps my callback with async
, and an additional itf()
will wrap my callback in fakeAsync
.Am I missing an existing concept or syntax? Is there a better alternative?
import { async } from '@angular/core/testing';
describe('MyService', () => {
let service: MyService;
...
it('should get data', async( async() => {
// arrange
let expectedData = { answer: 42 };
// act
let data = await service.getDataAsync();
// assert
expect(data).toEqual(expectedData);
} ));
})
fakeAsync and tick Like async we wrap the test spec function in a function called fakeAsync . We call tick() when there are pending asynchronous activities we want to complete. Like the async function the fakeAsync function executes the code inside its body in a special fake async test zone.
async and await Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
Handling errors in async functions is very easy. Promises have the . catch() method for handling rejected promises, and since async functions just return a promise, you can simply call the function, and append a . catch() method to the end.
there are a couple different ways to handle async testing:
(doneFunction) => {async test here... then eventually call done();}
. It gives you fine-grain control over where your test ends, but makes you kinda handle errors on your own with done.fail(error)
.tick()
wherever in your code to simulate the passage of time and resolution of observables, promises, and other async functions. One downside: you can't do HTTP calls in this, since they would happen real-time.Although I've found each of the 3 methods has pros & cons, I have found #2 to be the most useful for creating a smooth flowing test that is easily readable. So although you could avoid the nested async code with using #1 or #3, I'm not sure the benefit would outweigh the costs. As far as your suggestions, you may want to consider submitting a feature request to the angular repo if its important to you.
For more info, I found this source to be quite helpful: https://medium.com/@michaelericksen_12434/angular-asynchronous-test-patterns-and-recipes-202cf7d47ec7. Hope that helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With