Unit Testing Promise.all in Angular. Unit testing asynchronous code in Angular.
Let’s say, this is your component code,
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CommonService } from './common.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
public firstAPIresponse:any;
public secondAPIresponse:any;
public thirdAPIresponse:any;
public errorMessage:any;
constructor(private http : HttpClient, private service : CommonService){}
ngOnInit(){
this.makeAPICall();
}
makeAPICall(){
Promise.all([this.service.firstAPICall(), this.service.secondAPICall(),this.service.thirdAPICall()])
.then((response) => {
this.parseResponse(response);
})
.catch((error) => {
this.errorMessage = error;
})
}
parseResponse(response : any){
if(!response || !Array.isArray(response)) return;
this.firstAPIresponse = response[0] ? response[0] : [];
this.secondAPIresponse = response[1] ? response[1] : [];
this.thirdAPIresponse = response[2] ? response[2] : [];
}
}
makeAPICall
is using Promise.all
which waits for the three API calls to finish. Once the promises inside Promise.all
resolves successfully, the callback function is executed which calls the parseResponse
method. If any of the promises rejects, the catch
callback is executed.
You can make use of spyOn to mock the service calls and return promise.
let firstAPICall = spyOn(service,"firstAPICall").and.resolveTo([]);
let secondAPICall = spyOn(service,"secondAPICall").and.resolveTo([]);
let thirdAPICall = spyOn(service,"thirdAPICall").and.resolveTo([]);
With the service mocks, here is how the unit test case looks like:
it('should call makeAPICall', () => {
const fixture = TestBed.createComponent(AppComponent);
const component = fixture.componentInstance;
let service = fixture.debugElement.injector.get(CommonService);
let firstAPICall = spyOn(service,"firstAPICall").and.resolveTo([]);
let secondAPICall = spyOn(service,"secondAPICall").and.resolveTo([]);
let thirdAPICall = spyOn(service,"thirdAPICall").and.resolveTo([]);
component.makeAPICall();
expect(component.firstAPIresponse).toEqual([]);
})
If you try running the above, code it won’t pass since the unit test would run before the asynchronous promise execution.
To fix it you’ll also need to use fakeAsync
and tick
to test asynchronous code. From the official documentation,
fakeAsync Wraps a function to be executed in the fakeAsync zone:
Here is the unit test case using fakeAsync
.
it('should call makeAPICall', fakeAsync( () => {
const fixture = TestBed.createComponent(AppComponent);
const component = fixture.componentInstance;
let service = fixture.debugElement.injector.get(CommonService);
let firstAPICall = spyOn(service,"firstAPICall").and.resolveTo([]);
let secondAPICall = spyOn(service,"secondAPICall").and.resolveTo([]);
let thirdAPICall = spyOn(service,"thirdAPICall").and.resolveTo([]);
component.makeAPICall();
tick();
expect(component.firstAPIresponse).toEqual([]);
}))
When the above code runs inside the fakeAsync
zone, all asynchronous code is queued in.
tick
method triggers the execution of all asynchronous code and hence the above test case works fine and passes successfully.
So, this is how you can unit test promise.all
or asynchronous code in your Angular application.