How to write unit tests for Angular code that uses the HttpClient?

Alain Chautard
Angular Training
Published in
3 min readJan 6, 2018

--

Photo by Kevin on Unsplash

One of the interesting additions that came about with the HttpClient in Angular is the HttpClientTestingModule.

Using that module, one can fully (unit)test a component or a service without using an actual server implementation to handle HTTP requests from the Angular application.

Let’s assume we want to test a service, more specifically the following method:

getAllContacts(): Observable<Contact[]>> {
return this.http.get<Contact[]>("http://.../data/contacts"));
}

The above method uses the HttpClient to fetch some data from a server. We would like to write a test that makes sure that the task is handled properly. Our basic expectations would look like this:

service.getAllContacts().subscribe(data => {
expect(data.pageInfo.totalRecordCount).toBe(21);
expect(data.pageInfo.pageNumber).toBe(0);
expect(data.data.length).toBe(7);
});

Now if we just use the actual service as is, it is going to make an HTTP request to the server.

Angular Certification exam

What we’re going to do instead is replace it with a mocked version provided by the HttpClientTestingModule:

beforeEach(() => {
TestBed.configureTestingModule({
providers: [ContactsService],
imports: [
HttpClientTestingModule
],
});
});

The above code uses the Angular TestBed to configure our environment under test, which is going to use the HttpClientTestingModule instead of the regular HttpClientModule.

Once we have done that, we can inject a reference to a HttpTestingController, which will allow us to control the behavior of our mocked HttpClient, by setting expectations and returning fake data for testing purposes. The test now looks like this:

it('expects service to fetch data with proper sorting',
inject([HttpTestingController, ContactsService],
(httpMock: HttpTestingController, service: ContactsService) => {
// We call the service
service.getAllContacts().subscribe(data => {
expect(data.pageInfo.totalRecordCount).toBe(21);
expect(data.pageInfo.pageNumber).toBe(0);
expect(data.data.length).toBe(7);
});
// We set the expectations for the HttpClient mock
const req = httpMock.expectOne('http://.../data/contacts');
expect(req.request.method).toEqual('GET');
// Then we set the fake data to be returned by the mock
req.flush({data: ...});
})
);

Let’s look at the above code in more details. First we inject an instance of the HttpTestingController in our test:

inject([HttpTestingController, ContactsService],
(httpMock: HttpTestingController, service: ContactsService)

Then we set the expectations for that mock — expect exactly one HTTP request for ‘http://.../data/contacts':

const req = httpMock.expectOne('http://.../data/contacts');

Then we expect the HTTP method to be GET:

expect(req.request.method).toEqual('GET');

Finally, we ask the mock to return some fake data with the flush method:

req.flush({data: ...});

Once we have that code in place, our test would run and succeed. But it wouldn’t check all of our expectations in terms of URL called, HTTP method, and all that.

In order to make this happen, we add the extra step below:

afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
httpMock.verify();
}));

The above will run after every single test to make sure that our httpMock expectations are met. Now we have a test that validates the behavior of our service without using an actual server!

My Name is Alain Chautard. I am a Google Developer Expert in Angular, as well as founding consultant and trainer at Angular Training where I help web development teams learn and become fluent with Angular. Check us out @AngularTraining!

If you enjoyed this Angular tutorial, please clap for it or share it. Your help is always appreciated.

--

--