Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a unit test for Http post,put,delete using MockBackend in Angular2?

So I am testing my angular2 components & services.

So far I have used mockBackend for mocking get request in service as below:

/* tslint:disable:no-unused-variable */

import { MockBackend } from '@angular/http/testing';
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from '@angular/http';
import { PagesService } from './pages.service';
import { tick, fakeAsync } from '@angular/core/testing/fake_async';
import { inject, TestBed } from '@angular/core/testing/test_bed';
import {GlobalService} from './../../shared/global.service';

describe('PagesService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {
          provide: Http, useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
            return new Http(backend, defaultOptions);
          }, deps: [MockBackend, BaseRequestOptions]
        },
        { provide: PagesService, useClass: PagesService },
        { provide: GlobalService, useClass: GlobalService },
        { provide: MockBackend, useClass: MockBackend },
        { provide: BaseRequestOptions, useClass: BaseRequestOptions }
      ]
    });
  });

  //should retrive all search results
  it('should retrieve all search results',
    inject([PagesService, MockBackend], fakeAsync((pagesService: PagesService, mockBackend: MockBackend) => {
      let res: Response;
      mockBackend.connections.subscribe(c => {
        expect(c.request.url).toBe('http://localhost:3200/pm/pages/');
        let response = new ResponseOptions({
          body: '[{"name": "Rapid"}, {"name": "NGBC"}]'});
          c.mockRespond(new Response(response));
        });
        pagesService.getAllpages().subscribe((response) => {
           res = response;              
        });
        tick();
        expect(res[0].name).toBe('Rapid');
     }))
  );
 it('should fetch by Page id',
  inject([PagesService, MockBackend], fakeAsync((pagesService: PagesService, mockBackend: MockBackend) => {
    let res;
    mockBackend.connections.subscribe(c => {
      let page_id:string='2';
      expect(c.request.url).toBe('http://localhost:3200/pm/pages/'+page_id);
      let response = new ResponseOptions({body: '[{"id": 1, "name": "Rapid"}, {"id": 2, "name": "NGBC"}]'});
      c.mockRespond(new Response(response));
    });
    pagesService.getPageById('2').subscribe((response) => {
      res = response;
   });
   tick();
   expect(res[1].name).toBe('NGBC');
   }))
 ); 
});

If you want to see my service.ts its as below:

export class PagesService {
    private headers = new Headers();
    public baseUrl:string;
    constructor(private http: Http,private _globalService:GlobalService) {
        this.baseUrl=this._globalService.baseUrl;
    }
    getAllpages() {
        return this.http.get(this.baseUrl + '/pm/pages/')
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    getPageById(page_id: string) {
        return this.http.get(this.baseUrl + '/pm/pages/' + page_id)
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    savePage(page: Object) {
        this.headers=new Headers();
        this.headers.append('Content-Type', 'application/json');
        let url = this.baseUrl+'/pm/pages/';
        let data={};
        data["data"]=page;
        return this.http.post(url, JSON.stringify(data),{headers: this.headers})
           .map((res: Response) => res.json()).catch(this.handleError);
    }

    updatePage(page: any) {
        this.headers=new Headers();
        this.headers.append('Content-Type', 'application/json');
        let url = this.baseUrl+'/pm/pages/'+page._id;
        let data={};
        data["data"]=page;
        return this.http.put(url, JSON.stringify(data),{headers: this.headers})
            .map((res: Response) => res).catch(this.handleError);
    }

    deletePage(page_id: string) {
         this.headers=new Headers();
         this.headers.append('Content-Type', 'application/json');
         let url = this.baseUrl+'/pm/pages/';
         return this.http.delete(url + page_id,{headers: this.headers,body: ''})
             .map((res: Response) => res).catch(this.handleError);
    }

    mergePage(page: Object) {
        return this.http.post(this.baseUrl + '/pm/pages/',JSON.stringify(page))
            .map((res: Response) => res.json()).catch(this.handleError);
    }

    handleError(error: any) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

from the above service, I have implemented getAllPages() & getPageById() methods in my service.spec.ts file correctly as they are working fine.

I want to know how to implement test cases for savePage() , updatePage() , deletePage() methods.

Any inputs?

Thanks in advance.

like image 963
Bhushan Gadekar Avatar asked Sep 09 '16 07:09

Bhushan Gadekar


1 Answers

You need to examine the internals of the methods and see what it's doing. This is how you test it's expected behavior.

  • You're setting the Content-Type header. So this is a requirement, and you should test this behavior. You can get the headers from the MockConnection#request

    backend.connections.subscribe((conn: MockConnection) => {
      let contentType = conn.request.headers.get('Content-Type');
      expect(contentType).not.toBeNull();
      expect(contentType).toEqual('application/json');
    });
    
  • You're setting the URL, so this is a requirement. This should be tested. You already

    expect(conn.request.url).toBe('...');
    
  • You're sending data. So this is a requirement. You should test to make sure the data is in the required format/structure

    let body = conn.request.json();
    expect(body.data).toEqual('page');
    

That's for the request part.

For the response part, you should be testing what your service does with that response.

  • If you are receiving a body, you should test it like you did with the GET request.

  • You are also handling the error with an error callback. You don't need to test what the call back does here. That should be it's own test. All we want to test is that the error handle is called from the method being tests. For that we should use a spy. We can spy in the handleError function, and then check that it has been called.

    spyOn(service, 'handleError').and.callFake(() => {});
    // make call
    tick();
    expect(service.handleError).toHaveBeenCalled();
    

    I couldn't find a case (using the MockConnection) where the catch method is called on your Http request, that's why I didn't post an example. I tried conn.mockError(new Error()), it didn't work. I tried throwing an error inside the connection, it didn't work. Maybe you can figure it out.

like image 153
Paul Samsotha Avatar answered Nov 15 '22 23:11

Paul Samsotha