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"}]
   }
 }
De Giovanni
  • 319
  • 1
  • 3
  • 13
  • Does this answer your question? [Angular HttpClient unit tests won't fail when comparing data inside of an asynchronous function](https://stackoverflow.com/questions/54295582/angular-httpclient-unit-tests-wont-fail-when-comparing-data-inside-of-an-asynch) – Nicolas Seiller Jun 09 '22 at 15:24

2 Answers2

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);
    });

Reza
  • 18,865
  • 13
  • 88
  • 163
  • 1
    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... – Michael R Mar 17 '22 at 10:17
  • @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) – Reza Mar 17 '22 at 16:18
  • 1
    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. – Michael R Mar 21 '22 at 15:41
  • 1
    @MichaelR I also see the `expect` passing without `data` and `expectedResult` matching and am not sure how to solve it. – ETLJ May 10 '22 at 18:40
  • To use done you need to pass it to the calback's parameters, as described in the jasmine documentation: https://jasmine.github.io/tutorials/async#callbacks. However if you're using jest using the `resolves` operator (see my answer) is a cleaner solution. – Nicolas Seiller Jun 09 '22 at 15:45
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);
});
Nicolas Seiller
  • 564
  • 1
  • 10
  • 20