Angular mock multiple HTTP calls
Asked Answered
R

3

9

I have one service which is returning the forkjoin of multiple http calls. I wanted to test this scenario.

    class CommentService{
     addComments(){

let ob1 = Observable.of({});
    let ob2 = Observable.of({});
if(any condition)
        ob1 = {this.http.post('/url/1')};
if(any condition)
            ob2 = {this.http.post('/url/2'};
        return Observable.forkJoin(ob1,ob2)
           }
     }

Above is my service class. How can i mock the http calls.

describe("CommentService", () => {
  let httpClient: HttpClient;
  let httpTestingController: HttpTestingController;
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule, HttpClientTestingModule],
      providers: [CommentService]
    });

    httpClient = TestBed.get(HttpClient);
    httpTestingController = TestBed.get(HttpTestingController);
  });

  it('addComments() call with normal and gate', inject([CommentService], (service: CommentService) => {


    let cmts = service.addComments();

    const reqGateComment = httpTestingController.expectOne('/url/1');
    expect(reqGateComment.request.method).toEqual('POST');

    const reqFactComment = httpTestingController.expectOne('/url/2');
    expect(reqFactComment.request.method).toEqual('POST');

    reqGateComment.flush({});
    reqFactComment.flush({});


    httpTestingController.verify();

    cmts.subscribe(results=>{
       expect(results.length).toEqual(2);
    });

  }));


});

I am getting the below test fail. CommentService addFactsAndComments() call with normal and gate

Error: Expected one matching request for criteria "Match URL:

 '/url/1", found none.
Recipient answered 3/6, 2018 at 11:22 Comment(1)
any updates on this?Dowsabel
F
4

I had a similar problem and I solved it using fakeAsync.

it('addComments() call with normal and gate', fakeAsync( inject([CommentService], (service: CommentService) => {

    service.addComments().subscribe(results=>{
        expect(results.length).toEqual(2);
     });

    const reqGateComment = httpTestingController.expectOne('/url/1');
    expect(reqGateComment.request.method).toEqual('POST');
    reqGateComment.flush({});

    tick(10000);

    const reqFactComment = httpTestingController.expectOne('/url/2');
    expect(reqFactComment.request.method).toEqual('POST');
    reqFactComment.flush({});


    httpTestingController.verify();

  })));
Footloose answered 3/3, 2021 at 22:56 Comment(2)
I'm currently on Angular 11 and I can confirm this works for me as wellGlockenspiel
it does work, but why in heavens name mocking only works if called after the tested function... where is this on docs? :cry:Behling
D
0

Thats because you are wrapping the observables created by the HttpClient in new ones by using the of method.

By doing

joined$ = forkJoin(obs$(post1$),obs$(post2$))

Note: $ stands for observable

You create a new observable that:

  • Subscribes to the outer obs$
  • Waits for them to complete,
  • Collects their last emited values (in this case post1$ and post2$)
  • Returns the collected values as an array in the order of the source streams

Because we only subscribed to the outer obs$, your test is failing with

'/url/1", found none.

As we never subscribed to the inner post$, meaning that the requests werent sent.

Change your service method to:

addComments(){    
    const ob1 = this.http.post('/url/1');
    const ob2 = this.http.post('/url/2');

    return Observable.forkJoin(ob1,ob2);
 }
Dowsabel answered 4/6, 2018 at 8:12 Comment(1)
I updated my code. It is still throwing the same error.Recipient
G
0

You need to call addComments and subscribe to the returned observable before you invoke expectOne. Only on subscription will the httpClient requests be triggered

That's the core issue here that's causing the test failure

You're right not to use fakeAsync btw as using HttpClientTestingModule is synchronous

Garbo answered 14/4, 2021 at 20:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.