New HttpClient in Angular 4.3.x DRYs up Network Calls AND Testing

by
Tags: , , ,

It’s Deja Vu all over again — Yogi Barra

Thank the Maker, this Oil Bath is going to feel so good — C3P0

Ok, maybe I’m a little too excitable these days. But when I found out the Angular team added a better Http client to Angular 4.3.1 and higher, I was a tad estatic. This new client does several things that I’ve discovered on the surface: it makes handling result data easier, and it also makes testing closer to what we had with $httpClient in AngularJS 1.x!

DRYing up Http

Let’s look at some code we had in a demo sample in Angular 4, before the 4.3.1 release. This code fetches and maps an array of “customers”:

load(): Observable<Customer[]> {
  return this.http.get('/customers')
         .map(payload => payload.json());
}

That was a slice of code that fetched some customers and converted them into a domain object. You could expand it to provide mapping of specific fields / translation, but at least this is something typical of the original Http API.

now, look at the replacement using the new HttpClient:

load(): Observable<Customer[]> {
  return this.httpClient.get<Customer[]>('/customers');
}

What we’re missing here is the need to pluck the payload out of the original request. We just state our intent by telling the get method what type we want returned. Nice, eh? There’s a lot more in the API. But we’ll just focus on this feature for starters.

Installing the HttpClient API

To install `HttpClient` you just add it as an import:

import { HttpClientModule } from '@angular/common/http';
...
@NgModule({
  ...
  imports: [
    ...
    HttpClientModule
  ],
  ...
})
export class AppModule { }

Now you can use the new HttpClient API, which has an all-new developer guide. Features include caching and a true Http interceptor.

Testing gets better too!

Anyone who tried to test the old `Http` service found it rather frustrating, undocumented and manual. The Angular team didn’t port over a credible backend testing API! But now, after version 4.3.1, we have one. Here is a simple test exercising that service:

import {CustomerRESTService} from './customer-rest.service';
import {TestBed, inject} from '@angular/core/testing';
import {HttpClientTestingModule, HttpTestingController}
  from '@angular/common/http/testing';
import {HttpClient} from '@angular/common/http';
describe('Customer REST service', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [CustomerRESTService]})
    });
    it('should be a service', inject([
      HttpTestingController, CustomerRESTService
    ], (httpMock, customerRESTService) => {
    // call our service, and once the results come in
    // expect them to have the proper data (filled in
    // using the mock below)
    customerRESTService
      .load()
      .subscribe(customers => {
        expect(customers).toBeDefined();
        expect(customers.length).toBe(2);
        expect(customers).toEqual([
          {
            id: 1,
            name: 'Foo',
            numSales: 100
          }, {
            id: 2,
            name: 'Bar',
            numSales: 200
          }
        ]);
      });
    // look up our request and access it
    const request = httpMock.expectOne('/customers');
    // verify it is a GET
    expect(request.request.method).toEqual('GET');
    // Now, provide the answer to the caller above,
    // flushing the data down the pipe to the caller and
    // triggering the test's subscribe method
    request.flush([
      {
        id: 1,
        name: 'Foo',
        numSales: 100
      }, {
        id: 2,
        name: 'Bar',
        numSales: 200
      }
    ]);
    // make sure it actually got processed...
    httpMock.verify();
  }));
});

The machinery here is a bit odd, but it works. The HttpTestingController (which we’ve called httpMock like the developer guide did) is our API for generating fake data and forcing it down the line to the client. When the client (or a service that calls it) is triggered, it pauses, registering its intents with the mock backend. The lookup of the request gives us a way to flush the answer back to the caller and forces it to emit values. The verify() method makes sure we actually flushed any pending expects.

Plan on moving your services to HttpClient

If you want to take advantage of easy testing, simple result casting, caching, intercepting HTTP output and being able to test the API easily, you’ll want to convert over to HttpClient. Remember, you can only use this API in version 4.3.1 and above.

Like learning Angular from us? Let us come to you!

Chariot Solutions is a consulting firm first, and a training outfit in support of technologies we love. We use Angular on projects here at Chariot, and we also teach it. If you’re interested in onsite training for your company, read about our private, on-site Angular 4 training course.