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');
})
});
});
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With