Angular Test HTTP POST

By Arvind Rai, March 04, 2020
This page will walk through Angular unit testing for HTTP POST method using HttpClient.post. Angular uses Jasmine test framework and Karma test runner to create and run unit test cases. Angular provides HttpClientTestingModule for testing HTTP requests with HttpClient class. Using Angular HttpTestingController we can mock flushing of HTTP requests. Angular provides TestBed that is the primary API to write Angular tests. The TestBed configures components and services in application test module and instantiates them handling their dependency injection. Angular test codes are written in .spec.ts file which name is sibling to actual file and is in same folder. If our actual file is employee.service.ts then test file will be employee.service.spec.ts in same folder.
Here on this page we will create example to test a service class which has a method to POST data using HttpClient.post method.

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. HttpClientTestingModule

The HttpClientTestingModule is the Angular testing module used to test HTTP methods using HttpClient. The HttpClientTestingModule injects HttpTestingController that helps to expect and flush requests in our tests. The HttpClientTestingModule is imported as following.
import { HttpClientTestingModule } from '@angular/common/http/testing'; 

3. HttpTestingController

The HttpTestingController allows mocking and flushing of HTTP requests. It can be imported as following.
import { HttpTestingController } from '@angular/common/http/testing'; 
The HttpTestingController provides methods as match, expectOne, expectNone, verify. In our demo we will use following methods.
1.
abstract verify(opts?: { ignoreCancelled?: boolean; }): void 
It verifies that no unmatched requests are outstanding. We use it as following.
afterEach(() => {
  httpTestingController.verify();
}); 
2.
abstract expectOne(url: string, description?: string): TestRequest 
It expects that single request has been made matching given URL and returns mock result as TestRequest. It is used as following.
const req = httpTestingController.expectOne(empService.empUrl); 
Here req is the instance of TestRequest.

TestRequest

The TestRequest is a mock request. It has methods as flush, error, event. In our demo we will use its following methods.
1.
flush(body, opts): void 
Where body is string, number, object etc. and opts is optional.
The flush is used to resolve the request by returning a body. For example,
const req = httpTestingController.expectOne(empService.empUrl);
req.flush(newEmp); 
Here req is the object of type TestRequest. When we subscribe the request, the HttpClient method will return the specified object i.e. newEmp.
2.
event(event: HttpEvent<any>): void 
It delivers a HttpEvent on the response stream for this request. The HttpEvent is the type of all possible events on the response stream such as HttpResponse, HttpHeaderResponse etc. The event is used as following.
const expectedResponse = new HttpResponse({ status: 201, statusText: 'Created', body: newEmp });
req.event(expectedResponse); 
When we subscribe the request, the HttpClient method will return expectedResponse.
The TestRequest has a request property of HttpRequest type that can be used to get request information. For example,
req.request.method 

4. TestBed

The TestBed is the primary API for writing Angular unit tests. Find some of its methods.
1.
configureTestingModule(moduleDef: TestModuleMetadata): void 
It configures components, services in testing module required for testing. Find the code snippet to use it.
TestBed.configureTestingModule({
  imports: [HttpClientTestingModule],
  providers: [
	EmployeeService
  ]
}); 
2.
inject<T>(token: Type<T>, flags?: InjectFlags): T | null 
Instantiates required components or services by injecting the dependencies. For example,
httpClient = TestBed.inject(HttpClient);
httpTestingController = TestBed.inject(HttpTestingController);
empService = TestBed.inject(EmployeeService); 
The inject method injects the dependencies. In the EmployeeService, its constructor has the dependency of HttpClient. While instantiating EmployeeService, the inject method injects mock HttpClient.

The TestBed is imported as following.
import { TestBed } from '@angular/core/testing'; 

5. Jasmine Test Framework

Angular test application is created with Jasmine test framework. Jasmine is a JavaScript based test framework. Angular CLI downloads and installs everything required to run Jasmine test in our Angular application. Find the sample code to write Jasmine test.
describe("Test name", function() {
  beforeEach(() => {
      //TODO
  });
  afterEach(() => {
      //TODO
  });
  var msg;
  it("Test description", function() {
    msg = 'Hello';
    expect(msg).toEqual('Hello');
  });
}); 
Find some of the Jasmine methods.
describe() : Creates a group of specs.
beforeEach() : Executes before each of the specs in the describe in which it is called.
afterEach() : Executes after each of the specs in the describe in which it is called.
it() : Defines a single spec.
expect() : Creates an expectation for a spec.
fail() : It marks a spec as failed.

Angular uses Karma test runner which is downloaded and installed by Angular CLI by default. Karma is a test runner for JavaScript based test cases. It can be fine-tuned by editing karma.conf.js and src/test.ts files.

6. Example: Testing HttpClient.post

Find the service class with a method to post data.
employee.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';

import { map, catchError, tap } from 'rxjs/operators';
import { Employee } from './employee';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json',
    'Authorization': 'my-auth-token'
  })
};

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

  addEmployee(emp: Employee): Observable<Employee> {
    return this.http.post<Employee>(this.empUrl, emp, httpOptions)
      .pipe(
        tap(employee => console.log("employee: " + JSON.stringify(employee))),
        catchError(this.handleError(emp))
      );
  }
  private handleError<T>(result = {} as T) {
    return (error: HttpErrorResponse): Observable<T> => {
      console.error(error);
      return of(result);
    };
  }
} 
employee.ts
export interface Employee {
    name: string;
    age: number;
} 
Find test 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.addEmploye()', () => {
  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.
  });
  
  //Test case 1
  it('should add an employee and return it', () => {
    const newEmp: Employee = { name: 'Mahesh', age: 25 };

    empService.addEmployee(newEmp).subscribe(
      data => expect(data).toEqual(newEmp, 'should return the employee'),
      fail
    );

    // addEmploye should have made one request to POST employee
    const req = httpTestingController.expectOne(empService.empUrl);
    expect(req.request.method).toEqual('POST');
    expect(req.request.body).toEqual(newEmp);

    // Expect server to return the employee after POST
    const expectedResponse = new HttpResponse({ status: 201, statusText: 'Created', body: newEmp });
    req.event(expectedResponse);
  });

  //Test case 2
  it('should turn 404 error into return of the requested employee', () => {
    const newEmp: Employee = { name: 'Mahesh', age: 25 };

    empService.addEmployee(newEmp).subscribe(
      data => expect(data).toEqual(newEmp, 'should return the employee'),
      fail
    );

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

    // respond with a 404 and the error message in the body
    const msg = '404 error';
    req.flush(msg, { status: 404, statusText: 'Not Found' });
  });
}); 

7. 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. 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 POST

8. References

Angular Testing
Angular HttpClient
JASMINE Test

9. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us