Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test NestJs http request with full coverage?

I am having trouble to test my NestJs Service. I wrote a method, which does a GET http request:

getEntries(): Observable<Entries[]> {
    Logger.log(`requesting GET: ${this.apiHost}${HREF.entries}`);
    return this.http.get(`${this.apiHost}${HREF.entries}`).pipe(
      catchError((error) => {
        return throwError(error);
      }),
      map(response => response.data)
    );
  }

I want to write a unit test for this method. This unit test should cover all lines of this method. I tried to use "nock" package to mock this http request, but no matter how i try the coverage result is always the same.

return throwError(error);

map(response => response.data);

This two lines were uncovered.

Here my test file:

describe('getEntries method', () => {
    it('should do get request and return entries', () => {
      nock('http://localhost:3000')
        .get('/v1/entries')
        .reply(200, {
          data: require('../mocks/entries.json')
        });
      try {
        const result = service.getEntries();
        result.subscribe(res => {
          expect(res).toEqual(require('../mocks/entries.json'));
        });
      } catch (e) {
        expect(e).toBeUndefined();
      }
    });
    it('should return error if request failed', () => {
      nock('http://localhost:3000')
        .get('/v1/entries')
        .replyWithError('request failed');
      service.getEntries().subscribe(res => {
        expect(res).toBeUndefined();
      }, err => {
        expect(err).toBe('request failed');
      })
    });
  });
like image 801
Lukas Avatar asked Apr 29 '20 10:04

Lukas


Video Answer


2 Answers

I haven't used nock in the past, but you can just tell the HttpService what to respond with when using jest.spyOn. The biggest thing to remember is that the return is "synchronous", because it is a return of an observable. For testing your positive case, you can do something like

it('should do the request and get the entries', (done) => {
  const httpSpy = jest.spyOn(httpService, 'get')
    .mockReturnValue(of({data: require('../mocks/entries.json')}))
  let data = {};
  service.getEntires().subscribe({
    next: (val) => {data = val},
    error: (err) => { throw error; }
    complete: () => {
      expect(data).toEqual(require('../mocks/entries.json'))
      done();
    }
});

And similarly, for the error route, you could use the throwError() operator.

Both of and throwError are imported from rxjs. The only thing to note is that with this method, you do need to get the HttpService from the current module context, similarly to how you would get any other service. Just making sure to call that out.

like image 179
Jay McDoniel Avatar answered Oct 18 '22 19:10

Jay McDoniel


Thank you for your reply. The code works fundamentally. I had to make a few changes for the test to work. Here are my changes:

describe('getEntries method', () => {
    it('should do get request and return entries', (done) => {
      jest.spyOn(service['http'], 'get').mockReturnValue(of({data: require('../mocks/entries.json'), status: 200, statusText: 'OK', headers: {}, config: {}}));
      let data = {};

      service.getEntries().subscribe({
        next: (val) => {data = val},
        error: (err) => { throw err; },
        complete: () => {
          expect(data).toEqual(require('../mocks/entries.json'))
          done();
        }
      });
    });
    it('should return error if request failed', (done) => {
      jest.spyOn(service['http'], 'get').mockReturnValue(throwError('request failed'));
      let data = {};

      service.getEntries().subscribe({
        next: (val) => {data = val},
        error: (err) => {
          expect(err).toBe('request failed');
          done();
        },
        complete: () => {
          expect(data).toBeUndefined();
          done();
        }
      });
    });
  });

You have to mock AxiosReponse on httpSpy mockReturnValue, so i added 'status', 'statusText', 'header', 'config'. Otherwise you will get a type error.

And second part. I was spying on httpService like this:

let httpService: HttpService;
httpService = module.get(HttpService)

This doesn't work. I have to spy on the HttpService injection of my Service.

constructor(private readonly http: HttpService) {}

That' why my spy looks like: service['http'].

Now i have full coverage on this http request :)

Thank you so much ;) Have a nice day!

like image 27
Lukas Avatar answered Oct 18 '22 19:10

Lukas