Test Cases For HTTP GET Method Services In Angular

What is Angular testing?

Angular testing is a core feature available in every project set up with Angular CLI. An Angular unit test aims to uncover issues such as incorrect logic, misbehaving functions, etc. You know that testing each possible behavior would be very tedious, insufficient, and ineffective. One of the easiest ways to solve this problem is by writing test cases for each block. Do you need to wait until your users complain that what data they get when clicking on the button? By writing test cases for your blocks(components, services, etc.) you can easily detect where is a break!

Get started with Angular testing:

When we create a new project, it also downloads and installs everything we need to test an application. Angular uses the Jasmine test framework for testing an application and Karma test runner for run the test.

Test file has an extension of .spec.ts. It is in the same folder for a file for which we are writing tests.

To run the test, write the following command.

ng test

When we run this command, the test result is shown in the command prompt. The above command also opens the chrome browser and shows the test results.

http://localhost:9876

1. Jasmine:

Jasmine is the test framework for test the javascript code. Jasmine is a dependent free which means it can be run without DOM.

Following is the basic example of test code.

describe("Name of the test", function() {
  var msg;
  it("Test description", function() {
    msg = 'Hello';
    expect(msg).toEqual('Hello');
  });
});

Inbuilt method of Jasmine to use in creating test cases.

describe(): describe(string, function) functions take a title and a function containing one or more specs and are also known as a suite or test suite. it(string, function) functions take a title and a function containing one or more expectations and are also known as specs.

beforeEach(): As the name implies, the beforeEach function is called once before each spec in the describe().

afterEach(): As the name implies, the afterEach function is called once after each spec in the describe().

it():  Defines a single spec. It contains one or more than one expects.

expects(): it describes an expected piece of behavior in the application. i.e., what we want in response.

fail(): It marks a test failed.

2. Karma:

Karma is a test runner for javascript applications. When we run the ng test command, it builds the app in watch mode and it launches the Karma test runner.

3. HttpTestingController:

The HttpTestingController is to be injected into tests, which allows for mocking and flushing of requests. It can be imported as follows.
import { HttpTestingController } from '@angular/common/http/testing';
The HttpTestingController has the following methods.
match(): Search for requests that match the given parameter, without any expectations.
expectOne(): Expect that a single request has been made which matches the given URL, and return its mock. If no such request has been made, or more than one such request has been made, fail with an error message including the given request description, if any.
expectNone(): Expect that no requests have been made which match the given URL. If a matching request has been made, fail with an error message including the given request description, if any.
verify(): Verify that no unmatched requests are outstanding. If any requests are outstanding, fail with an error message indicating which requests were not handled.

4. HttpClientTestingModule:

The HttpClientTestingModule injects HttpTestingController to expect and flush requests in our tests. It is imported as follows.

import { HttpClientTestingModule } from '@angular/common/http/testing';

5. TestBed

The TestBed is the primary API for writing unit tests for Angular applications and libraries. It configures and initializes the environment for unit testing and provides methods for creating components and services in unit tests. It is imported as follows.
import { TestBed } from '@angular/core/testing';

configureTestingModule(): Allows overriding default providers, directives, pipes, modules of the test injector, which are defined in test_injector.js

inject(): Instantiates components, services, and other classes required for testing.

Testing GET Method:

In this example, we will test the HTTP GET method using HttpClient.get method.

employee.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';
import { Employee } from './employee';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {
  empUrl = "/api/employees";
  constructor(private http: HttpClient) { }

  getAllEmployees(): Observable<Employee[]> {
    return this.http.get<Employee[]>(this.empUrl).pipe(
      tap(employees => console.log("employees: " + JSON.stringify(employees))),
      catchError(this.handleError<Employee[]>([]))
    );
  }

  private handleError<T>(result = {} as T) {
    return (error: HttpErrorResponse): Observable<T> => {
      console.error(error);
      return of(result);
    };
  }
}

employee.ts

export interface Employee {
    id: number;
    name: string;
}

We will test getAllEmployees() method of employee service. We are testing the following scenario.

1.  Should return expected employees by calling once.

2.  should be OK returning no employee.

3.  should return expected employees when called multiple times.

employee.service.spec.ts

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { TestBed } from '@angular/core/testing';
import { HttpClient, HttpResponse } from '@angular/common/http';

import { Employee } from './employee';
import { EmployeeService } from './employee.service';

//Testing of EmployeeService
describe('EmployeeService', () => {
  let httpClient: HttpClient;
  let httpTestingController: HttpTestingController;
  let empService: EmployeeService;

  beforeEach(() => {
    //Configures testing app module
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        EmployeeService
      ]
    });

    //Instantaites HttpClient, HttpTestingController and EmployeeService
    httpClient = TestBed.inject(HttpClient);
    httpTestingController = TestBed.inject(HttpTestingController);
    empService = TestBed.inject(EmployeeService);
  });

  afterEach(() => {
    httpTestingController.verify(); //Verifies that no requests are outstanding.
  });

  describe('#getAllEmployees', () => {
    let expectedEmps: Employee[];

    beforeEach(() => {
      //Dummy data to be returned by request.
      expectedEmps = [
        { id: 2001, name: 'John' },
        { id: 2002, name: 'Wick' },
      ] as Employee[];
    });
    
    //Test case 1
    it('should return expected employees by calling once', () => {
      empService.getAllEmployees().subscribe(
        emps => expect(emps).toEqual(expectedEmps, 'should return expected employees'),
        fail
      );

      const req = httpTestingController.expectOne(empService.empUrl);
      expect(req.request.method).toEqual('GET');

      req.flush(expectedEmps); //Return expectedEmps
    });
    
    //Test case 2
    it('should be OK returning no employee', () => {
      empService.getAllEmployees().subscribe(
        emps => expect(emps.length).toEqual(0, 'should have empty employee array'),
        fail
      );

      const req = httpTestingController.expectOne(empService.empUrl);
      req.flush([]); //Return empty data
    });

    //Test case 3
    it('should return expected employees when called multiple times', () => {
      empService.getAllEmployees().subscribe();
      empService.getAllEmployees().subscribe(
        emps => expect(emps).toEqual(expectedEmps, 'should return expected employees'),
        fail
      );

      const requests = httpTestingController.match(empService.empUrl);
      expect(requests.length).toEqual(2, 'calls to getAllEmployees()');

      requests[0].flush([]); //Return Empty body for first call
      requests[1].flush(expectedEmps); //Return expectedEmps in second call
    });
  });
});

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories