Angular 7 Testing - Async function call + async..await
Asked Answered
U

1

5

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:

  1. it() should magically support async..await and wrap my callback in async() so that I don't have to think about it.
  2. it() should take an optional function parameter (i.e., async or fakeAsync) that will wrap my callback.
  3. it() variations ita() and itfa() should exist that will wrap my callback with the appropriate async helper.
  4. 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);
        } ));
    })
Upbear answered 2/3, 2019 at 22:25 Comment(1)
As far as being "confusing", maybe it's a bit simpler to understand now that they've renamed async() as waitForAsync()?Ivanovo
F
1

there are a couple different ways to handle async testing:

  1. Use the built-in karma doneFunction: (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).
  2. Wrap in the angular async() function. This is part of your example above, but as you noted, it seems that the Angular async function doesn't automatically support await syntax inside, thus necessitating the use of the inner async to get support for await.
  3. Use the Angular fakeAsync() wrapper function, which allows you to call 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!

Floodgate answered 31/3, 2020 at 1:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.