Angular 2+ provides async
and fakeAsync
utilities for testing asynchronous code. This should make your Angular unit and integration tests that much easier to write.
In this article, you will be introduced to waitForAsync
and fakeAsync
with sample tests.
To complete this tutorial, you will need:
This tutorial was verified with Node v16.4.0, npm
v7.19.0, and @angular/core
v12.1.1.
First, use @angular/cli
to create a new project:
Then, navigate to the newly created project directory:
This will create a new Angular project with app.component.html
, app.compontent.ts
, and app.component.spec.ts
files.
waitForAsync
The waitForAsync
utility tells Angular to run the code in a dedicated test zone that intercepts promises. We briefly covered the async utility in our intro to unit testing in Angular when using compileComponents
.
The whenStable
utility allows us to wait until all promises have been resolved to run our expectations.
First open app.component.ts
and use a Promise
to resolve
the title
:
Then open app.component.html
and replace it with a h1
and button
:
When the button is clicked, the title
property is set using a promise.
And here’s how we can test this functionality using waitForAsync
and whenStable
:
Note: In a real app you will have promises that actually wait on something useful like a response from a request to your backend API.
At this point, you can run your test:
This will produce a successful 'should display title'
test result.
fakeAsync
The problem with async
is that we still have to introduce real waiting in our tests, and this can make our tests very slow. fakeAsync
comes to the rescue and helps to test asynchronous code in a synchronous way.
To demonstrate fakeAsync
, let’s start with a simple example. Say our component template has a button that increments a value like this:
It calls an increment
method in the component class that looks like this:
And this method itself calls a method in an incrementDecrement
service:
That has an increment
method that’s made asynchronous with the use of a setTimeout
:
Obviously, in a real-world app this asynchronicity can be introduced in a number of different ways.
Let’s now use fakeAsync
with the tick
utility to run an integration test and make sure the value is incremented in the template:
Notice how the tick
utility is used inside a fakeAsync
block to simulate the passage of time. The argument passed-in to tick
is the number of milliseconds to pass, and these are cumulative within a test.
Note: Tick
can also be used with no argument, in which case it waits until all the microtasks are done (when promises are resolved for example).
At this point, you can run your test:
This will produce a successful 'should increment in template after 5 seconds'
test result.
Specifying the passing time like that can quickly become cumbersome, and can become a problem when you don’t know how much time should pass.
A new utility called flush
was introduced in Angular 4.2 and helps with that issue. It simulates the passage of time until the macrotask queue is empty. Macrotasks include things like setTimouts
, setIntervals
, and requestAnimationFrame
.
So, using flush
, we can write a test like this for example:
At this point, you can run your test:
This will produce a successful 'should increment in template'
test result.
In this article, you were introduced to waitForAsync
and fakeAsync
with sample tests.
You can also refer to the official documentation for an in-depth Angular testing guide.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Thank you, that was helpful