Angular Test HTTP GET Request

By Arvind Rai, March 02, 2020
This page will walk through Angular unit testing for HTTP GET request using HttpClientTestingModule and TestBed API. We will test Angular HttpClient.get method in our example.
1. When we create the project using CLI, it downloads and installs everything we need to test an Angular application. Angular uses Jasmine test framework. To test Angular application, we need to run following command.
ng test 
The test result can be seen in command prompt as well as in browser. The above command will start Chrome browser and display test result. We can also use below URL to see test result in any browser.
http://localhost:9876/ 
2. Angular test uses Karma test runner. Angular CLI configures Jasmine and Karma by default. We can fine-tune configurations using karma.conf.js and the src/test.ts file.

3. For Angular testing, we need to use following Angular testing library.
@angular/core/testing contains core testing API such as TestBed.
@angular/common/http/testing contains HTTP testing API such as HttpClientTestingModule and HttpTestingController.

1. Technologies Used

Find the technologies being used in our example.
1. Angular 9.0.2
2. Node.js 12.5.0
3. Jasmine Core 3.5.0
4. Karma 4.3.0

2. .spec.ts file for Unit Test

Test file name is sibling and is in same folder for a file for which we are writing tests. We need to create test file name by appending .spec to the original file. Find some examples.
Component
|--app.component.ts
|--app.component.spec.ts 
Service
|--employee.service.ts
|--employee.service.spec.ts 

3. Jasmine Test Framework

Jasmine is a test framework to test JavaScript code. It can run without a DOM. We write test code as following.
describe("Test name", function() {
  var msg;
  it("Test description", function() {
    msg = 'Hello';
    expect(msg).toEqual('Hello');
  });
}); 
Find some methods of Jasmine to use in creating unit test cases.
describe() : Creates a group of specs. The call to describe can be nested.
beforeEach() : It is executed before each of the specs in the describe in which it is called.
afterEach() : It is executed after each of the specs in the describe in which it is called.
it() : Defines a single spec. It contains one or more than one expect.
expect() : Creates an expectation for a spec.
fail() : It marks a spec as failed.

When we create Angular project using Angular CLI, it downloads Jasmine dependencies by default. We can look into our package.json file for the Jasmine dev dependencies.
package.json
{
  ------
  "devDependencies": {
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
     ------
  }
} 

Karma test runner
When we run ng test command, it builds app in watch mode and it launches Karma test runner. Karma is a test runner for JavaScript application. The Angular CLI takes care of Jasmine and Karma configuration for us. We can fine-tune our configurations by editing karma.conf.js and test.ts files. Their locations are as following.
 my-app
  |
  |--karma.conf.js
  |
  |--src
  |   |
  |   |--test.ts 

4. HttpTestingController

The HttpTestingController is a controller to be injected into tests that allows for mocking and flushing of requests. The HttpTestingController has following methods.
match() : Search for the requests that matches the given URL. This method expects nothing.
expectOne() : Expects that the request for the matching given URL has been made only one time and returns TestRequest.
expectNone : Expects that for the matching given URL, no request has been made.
verify : Verifies that no requests are outstanding.

The HttpTestingController can be imported as following.
import { HttpTestingController } from '@angular/common/http/testing'; 

5. HttpClientTestingModule

The HttpClientTestingModule injects HttpTestingController to expect and flush requests in our tests. It is imported as following.
import { HttpClientTestingModule } from '@angular/common/http/testing'; 

6. TestBed

The TestBed is the primary API used in Angular testing applications. The TestBed configures and initializes test environment. It also creates components and services required for unit testing. We will use some of its methods in our unit tests as given below.
configureTestingModule() : Configures testing module. It can import required modules, declare required components and can provide required services.
inject() : Instantiates components, services and other classes required for testing.

The TestBed is imported in our test spec file as following.
import { TestBed } from '@angular/core/testing'; 

7. TestRequest

The TestRequest is a mock request which is ready to be answered. It access to the underlying HttpRequest and allows responding as response data or error. The TestRequest is returned by expectOne() method of HttpTestingController. The TestRequest has following methods.
flush() : Handles the request by returning HTTP body and headers.
error() : Returns error for the request.
event() : Deliver an arbitrary HttpEvent.

The TestRequest has also request property of HttpRequest type using which we can get request information, such as HTTP method name.

8. Example: Testing HttpClient.get

In our unit test case, we will test a service class which will contain a method for HTTP GET using HttpClient.get method. Find the service class.
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 following scenarios for getAllEmployees() method of EmployeeService class. Find the unit test cases.
Test case 1: Should return expected employees by calling once.
Test case 2: should be OK returning no employee.
Test case 3: should turn 404 error into an empty employee result.
Test case 4: should return expected employees when called multiple times.

Find the spec file.
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
      ]
    });

    //Instantiates 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: 101, name: 'Krishna' },
        { id: 102, name: 'Arjun' },
      ] 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 turn 404 error into an empty employee result', () => {
      empService.getAllEmployees().subscribe(
        emps => expect(emps.length).toEqual(0, 'should return empty employee array'),
        fail
      );

      const req = httpTestingController.expectOne(empService.empUrl);

      const msg = '404 error';
      req.flush(msg, { status: 404, statusText: 'Not Found' }); //Return error
    });

    //Test case 4
    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
    });
  });
});
 
1. In test cases, when we subscribe to getAllEmployees(), the mock HttpClient will return data assigned to flush() method as below.
req.flush(expectedEmps); 
2. When test is not successful, then fail is executed. It throws error with error messages.
3. The expect() creates an expectation. For example.
expect(emps).toEqual(expectedEmps, 'should return expected employees') 
We are expecting here that emps should be equal to expectedEmps. If not, then test is fail and the fail method will execute.

9. Run Test Cases

To run the test cases, find the steps.
1. Install Angular CLI using link.
2. Download source code using download link given below on this page.
3. Use downloaded src in your Angular CLI application.
4. Run ng test using command prompt. Test result can be seen on command prompt as well as on browser.
5. Find the test output in command prompt.
Angular Test HTTP GET Request
6. A chrome for test result will open automatically or we can also use the URL http://localhost:9876/
Find the test result in the browser.
Angular Test HTTP GET Request

10. References

Angular Testing
Angular HttpClient
JASMINE Test

11. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us