Testing an Angular Http Service/Component

Every web application written to day has some sort of dependency on HTTP requests. We use them to grab all the data needed to implement our desired features. Because HTTP requests are the primary way of bringing data to our Angular app, testing services or components that have a dependency on HTTP is crucial. Fortunately for us, testing an Angular HTTP service/component is quite easy.

Example

For this example, I am using Angular 6. We will test an API Service that fetches data using HTTP. In order to test this service, we need to mock the HTTP part and not use actual HTTP requests in our tests.

ApiService.ts

import {Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {Notebook} from "../notes/model/notebook";
import {FeedbackViewModel} from "../feedback/feedback.component";
import {Note} from "../notes/model/note";

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  private BASE_URL = "localhost:8080/api";
  public ALL_NOTEBOOKS_URL = `${this.BASE_URL}/notebooks/all`;
  private SEND_FEEDBACK_URL = `${this.BASE_URL}/feedback`;
  private SAVE_UPDATE_NOTEBOOK = `${this.BASE_URL}/notebooks`;
  private DELETE_NOTEBOOK_URL = `${this.BASE_URL}/notebooks/`;
  private ALL_NOTES_URL = `${this.BASE_URL}/notes/all`;
  private NOTES_BY_NOTEBOOK_URL = `${this.BASE_URL}/notes/byNotebook/`;
  private SAVE_UPDATE_NOTE_URL = `${this.BASE_URL}/notes`;
  private DELETE_NOTE_URL = `${this.BASE_URL}/notes/`;

  constructor(private http: HttpClient) {

  }

  getAllNotebooks(): Observable<Notebook[]> {
    return this.http.get<Notebook[]>(this.ALL_NOTEBOOKS_URL);
  }

  postFeedback(feedback: FeedbackViewModel): Observable<any> {
    return this.http.post(this.SEND_FEEDBACK_URL, feedback);
  }

  postNotebook(notebook: Notebook): Observable<Notebook> {
    return this.http.post<Notebook>(this.SAVE_UPDATE_NOTEBOOK, notebook);
  }

  deleteNotebook(id: string): Observable<any> {
    return this.http.delete(this.DELETE_NOTEBOOK_URL + id);
  }

  getAllNotes(): Observable<Note[]> {
    return this.http.get<Note[]>(this.ALL_NOTES_URL);
  }

  getNotesByNotebook(notebookId: string): Observable<Note[]> {
    return this.http.get<Note[]>(this.NOTES_BY_NOTEBOOK_URL + notebookId);
  }

  saveNote(note: Note): Observable<Note> {
    return this.http.post<Note>(this.SAVE_UPDATE_NOTE_URL, note);
  }

  deleteNote(noteId:string):Observable<any>{
    return this.http.delete(this.DELETE_NOTE_URL + noteId);
  }
}

Notice that the ApiService has a dependency on HttpClient. HttpClient is the Angular service responsible for handling HTTP requests. This is the dependency that we want to mock in testing.

ApiService.spec.ts

import {TestBed, getTestBed} from '@angular/core/testing';

import {ApiService} from './api.service';
import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
import {Notebook} from "../notes/model/notebook";

describe('ApiService', () => {
  let httpMock: HttpTestingController;
  let apiService: ApiService;

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

    // define http mock
    httpMock = getTestBed().get(HttpTestingController);
    
    apiService = getTestBed().get(ApiService);
  });

  // test creation of ApiService
  it('it is created', ()=>{
    expect(apiService).toBeTruthy();
  });

  // test notebook retrieval using fake http with dummy data
  it('should get all notebooks from http', ()=>{
    // arrange
    let dummyNotebooks:Notebook[] = [
      {id:'1', name:'default', nbOfNotes:0}
    ];

    // act
    apiService.getAllNotebooks().subscribe(res => {
      expect(res.length).toBe(1);
      expect(res).toEqual(dummyNotebooks);
    });

    // http mock
    let req = httpMock.expectOne(apiService.ALL_NOTEBOOKS_URL);
    expect(req.request.method).toBe("GET");
    req.flush(dummyNotebooks);
  });

});

Note:

In our spec, we need to add import HttpTestingController. Our httpMock will be an instance of this class. Then, using the httpMock, we can set expectations on URL calls or fetched data. Note that we use the flush(dummyNotebooks) method to actually write the HTTP request results.

Source Code:

You can grab the source code for this example on GitHub: https://github.com/dangeabunea/RomanianCoderExamples/tree/master/NoteIt/noteit-ng-app/src/app/shared