How test request http with parameters with jest - angular
Asked Answered
B

3

7

I'm testing an angular service with Jest.

I can to mock my httpClient, but I have a problem it is that I cannot test a url with a parameter (ex: "http://localhost:8080/api/feature/num?id=25663").

This my test code :

describe('MyService', () => {
   let myService: MyService;
   const httpMock = {get: jest.fn(() => of(dataMock))};
   const provide = (mock: any): any => mock;

   beforeEach(() => {
      myService= new MyService(provide(httpMock));
   });

   test('should created', () => {
     expect(myService).toBeTruthy();
   });

   test('should retrieve one user', (done) => {
     const number = 26586;
     const url = 'http://localhost:8080/api/feature/num?number=25663';

     myService.getNumero(number).subscribe( 
       res => {
          expect(httpMock.get).toBeCalledWith(url);
          expect(res.number).toBe(number);
          expect(res.city).toEqual('PARIS');
          done();
     });
   });
});

And in my console I have this error :

Error: Uncaught [Error: expect(jest.fn()).toBeCalledWith(...expected)

Expected: "http://localhost:8080/api/feature/num?number=25663"
Received: "http://localhost:8080/api/feature/num, {
 "params": {
    "cloneFrom": {
        "cloneFrom": null, 
        "encoder": {}, 
        "map": null, 
        "updates": null
     },
     "encoder": {}, 
     "map": null,
     "updates": [{"op": "a", "param": "number", "value": "25663"}]
   }
 }
Blessed answered 29/1, 2020 at 17:13 Comment(1)
Does this answer your question? Angular HttpClient unit tests won't fail when comparing data inside of an asynchronous functionBohannon
B
6

Usually I do like this

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

...

describe('MyService', () => {
    let injector: TestBed;
    let httpMock: HttpTestingController;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [],
        });
        injector = getTestBed();
        httpMock = injector.get(HttpTestingController);
    });

    afterEach(() => {
        httpMock.verify();
    });

...

    it('should return number and city', () => {
        const expectedResult: {
            number: 26586,
            city: 'PARIS'
        };

        const service : MyService = TestBed.get(MyService);
        service.getNumero(number)
            .pipe(
                first(),
            )
            .subscribe((data) => {
                expect(data).toMatchObject(expectedResult);
            });

        const req = httpMock.expectOne(`http://localhost:8080/api/feature/num?number=25663`);
        expect(req.request.method).toBe('GET');
        req.flush(expectedResult);
    });

Balkan answered 31/1, 2020 at 15:0 Comment(6)
Tried this, and the test goes green, but you can replace 'expectedResult' in subcriber with anything, so it dosent really validate that the actual object is correct. Dont know how to solve it...Apocynaceous
@MichaelR this line in subscribe is checking it expect(data).toMatchObject(expectedResult); if you change it to something else it fails. for example if your service do some manipulation on the response before sending it back you need to check with that result (in this example both are same)Balkan
I've tested this setup, except I tested with a our api and switched to the observable our function returns and url and so on, also had to change 'injector.get' to 'injector.inject' since 'get' is deprecated. Its still the same issue that the expect in subscriber never runs, even if I set the done(); after the expect and in the beginning where the 'it' text is. Really strange.Apocynaceous
@MichaelR I also see the expect passing without data and expectedResult matching and am not sure how to solve it.Member
To use done you need to pass it to the calback's parameters, as described in the jasmine documentation: jasmine.github.io/tutorials/async#callbacks. However if you're using jest using the resolves operator (see my answer) is a cleaner solution.Bohannon
@MichaelR I have posted an answer that addresses that issue, as I had it also.Oaken
B
1

You can use jest's resolves operator:

it('should return number and city', () => {
    const expectedResult: {
        number: 26586,
        city: 'PARIS'
    };

    const service : MyService = TestBed.get(MyService);
    expect(service.getNumero(number).toPromise())
        .resolves.toMatchObject(expectedResult);

    const req = httpMock.expectOne(`http://localhost:8080/api/feature/num?number=25663`);
    expect(req.request.method).toBe('GET');
    req.flush(expectedResult);
});
Bohannon answered 9/6, 2022 at 15:18 Comment(0)
O
0

This is in response to the comments on the accepted answer.

I too had the same issue that when following the official Angular documentation test setup for Http requests, my expect inside was never actually executed and wasn't verifying my expected objet, however I found the solution using fakeAsync like so:

// define expected object outside tests
const expected = {...}

it('should validate request and response', fakeAsync(() => {
   // use last value RxJs operator to convert observable into promise
   lastValueFrom(service.doSomething()).then(res => expect(res).toEqual(expected));

   const req = httpTestingController.expectOne({
     method: 'GET',
     url: `${url}/doSomething`
   });

   // flush with expected body value
   req.flush(...);
   // run tick() or flush() to flush async queues and execute expected comparison
   tick();
}));

This will do the expected behavior we want. Hope it helps.

Oaken answered 5/1 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.