Home > Software engineering >  Angular unit test with fakeAsync and tick not waiting for code to execute with Observable
Angular unit test with fakeAsync and tick not waiting for code to execute with Observable

Time:01-27

In my Angular application, I am trying to write a unit test case in which I have mocked the rest service response as Observable. So, I have used fakeAsync & tick in my test case. But still it is failing.

service.ts

import { HttpClient } from "@angular/common/http";

@Injectable()
export class RestServices{

    constructor(private _http: HttpClient){}

    public post(body: any): void{
        // have variables initializations

        this._http
            .post(url, body, { observe: "response" })
            .subscribe(
                response => this._onSuccess(response),
                error => this._onFailure(error);
            );
    } 

    private _onSuccess(response: any){
        if(response.status == 200){
            // extract data from response
        }
    }

    private _onFailure(error: any){
        // code for failure
    }
}

service.spec.ts

import { TestBed, tick } from "@angular/core/testing";
import { of } from "rxjs/internal/observable/of";
import { Observable } from "rxjs/internal/Observable";


describe("Rest", () => {
    let _service: RestServices;

    beforeEach(() => {
        const httpSpyObject = jasmine.createSpyObj("HttpClient", {
            get: of(),
            post: of(new HttpResponse({ status: 200, body: "Text"})),
            put: of(),
        });
        TestBed.configureTestingModule({
            providers: [
                RestServices,
                { provide: HttpClient, useValue: httpSpyObject }
            ],
        });
        _service = TestBed.inject(RestServices);
    });

    it("should create", () => {
        expect(_service).toBeTruthy();
    });

   
    it("should call Success method on success response", fakeAsync(() => {
        const completeSpy = spyOn<any>(_service, "_onSuccess");
        const errorSpy = spyOn<any>(_service, "_onFailure");
        const subscrieSpy = spyOn(Observable.prototype, "subscribe");

        const body = { name: "abc" };

        _service.post(body);
        
        tick(1000);

        // Passing the below test case.
        expect(subscrieSpy).toHaveBeenCalled();

        // not passing below test case
        expect(completeSpy).toHaveBeenCalled();
    }));
});

Now, How can I pass the second test case to check that _onSuccess() has been called from subscribe ?

CodePudding user response:

I would use HttpClientTesingModule, it will make testing much easier.

describe('Rest', () => {
  let service: RestService;
  let httpTestingController: HttpTestingController;
  
  beforeEach(() => {
    TestBed.configureTestingModule({
       imports: [HttpClientTestingModule],
       providers: [RestService],
    });
    service = Testbed.inject(RestService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    // ensure all http calls are dealt with (none in queue)
    httpTestingController.verify();
  });

  it('creates', () => {
    expect(service).toBeTruthy();
  });

  it('should call success method on success response', () => {
    const successSpy = spyOn(service, 'onSuccess');
    // 2nd argument is the body
    service.post('urlGoesHere', {});

    const request = httpTestingController.expectOne('urlGoesHere');
    expect(request.request.method).toBe('Post');
    
    // send this for the response
    req.flush({});
    expect(successSpy).toHaveBeenCalled();
  });
 
});

Check out the above, hopefully it should get you started.

A question I have for you, is the post method takes a body but it doesn't take a URL? I think it should take a url as well.

Edit:

You may have to add .and.callThrough() to call the actual implementation of what you're spying on.

Like so:

const subscrieSpy = spyOn(Observable.prototype, "subscribe").and.callThrough();
  •  Tags:  
  • Related