Reputation: 701
I am calling a API in nestjs service like below,
import { HttpService, Post } from '@nestjs/common';
export class MyService {
constructor(private httpClient: HttpService) {}
public myMethod(input: any) {
return this.httpClient
.post<any>(
this.someUrl,
this.createObject(input.code),
{ headers: this.createHeader() },
)
.pipe(map(response => response.data));
}
}
How can I mock/spyOn the call to this.httpClient.post() in jest to return response without hitting the actual API?
describe('myMethod', () => {
it('should return the value', async () => {
const input = {
code: 'value',
};
const result = ['test'];
// spyOn?
expect(await myService.myMethod(input)).toBe(result);
});
});
Upvotes: 23
Views: 30410
Reputation: 408
If you are using axios
make sure in packakge.json add
"moduleNameMapper": {
"^@shared/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
"^@app/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
"^axios$": "<rootDir>/node_modules/axios/dist/axios.min.js"
}
Upvotes: -1
Reputation: 14893
As @Diaboloxx mentioned you should be mocking out your HttpService
via the providers
setup in your tests.
Ideally anything that you are dependency injecting into your constructor you should be providing and mocking out. That way your tests are isolated to the given file, and in this case to help prevent making real requests.
I like to use mockDeep
from the library: jest-mock-extended
to handle mocking on the providers to see what has been called on them, as well as to mock returned values. It's also nice because mockDeep
will enforce type safety to validate that you're mocking valid return data.
Side Note: it is good practice to test that your dependencies are being called with what you expect.
import { HttpService } from '@nestjs/axios'
import { Test, TestingModule } from '@nestjs/testing'
import { mockDeep } from 'jest-mock-extended'
import { of } from 'rxjs'
import { AxiosResponse } from 'axios'
import { MyService } from '@/services/my.service'
describe('MyService', () => {
let myService: MyService
const httpService = mockDeep<HttpService>()
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [myService],
providers: [
{
provide: HttpService,
useValue: httpService,
},
],
}).compile()
myService = app.get<MyService>(MyService)
})
describe('#myMethod', () => {
const response: AxiosResponse<unknown, any> = {
data: { hello: 'world' },
headers: {},
config: { url: 'http://localhost:3000/mockUrl' },
status: 200,
statusText: 'OK',
}
beforeEach(() => {
httpService.post.mockReturnValue(of(response))
})
it('should return "Hello World!"', async () => {
const result = await myService.myMethod({ code: 'value' })
expect(result).toEqual({ hello: 'world' })
})
it('calls httpService.post with the correct params', async () => {
await myService.myMethod({ code: 'value' })
expect(httpService.post).toHaveBeenLastCalledWith(
'someBaseUrl/param1/param2',
{ body: 'body' },
expect.objectContaining({
headers: {
header: 'header',
},
}),
)
})
})
})
Upvotes: 3
Reputation: 1256
I had a method that was using the result of my mocked post call, so I end up with this
describe('my test', function () {
let service: LegalTextAdminClientFactory;
const httpService = {
get: jest.fn(),
post: jest.fn().mockImplementation(() => of({ data: {} })),
};
const configService = {
get: jest.fn().mockReturnValue('mock'),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{ provide: HttpService, useValue: httpService },
{ provide: ConfigService, useValue: configService },
LegalTextAdminClientFactory,
],
}).compile();
service = await module.get(LegalTextAdminClientFactory);
});
});
so by returning this "of()" you can even pipe the result of it
Upvotes: 2
Reputation: 311
A good alternative to mocking the http service would also be to declare it in the providers array as follow.
let httpClient: HttpService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: HttpService,
useValue: {
post: jest.fn(() => of({
// your response body goes here
})),
},
},
],
}).compile();
httpClient = module.get<HttpService>(HttpService);
});
By providing your HttpService in the testing module rather than use spy on, you ensure that the HttpModule won't be imported or used and reduce your test code dependency to other services.
Upvotes: 16
Reputation: 701
Got it working by using spyOn.
describe('myMethod', () => {
it('should return the value', async () => {
const input = {
code: 'mock value',
};
const data = ['test'];
const response: AxiosResponse<any> = {
data,
headers: {},
config: { url: 'http://localhost:3000/mockUrl' },
status: 200,
statusText: 'OK',
};
jest
.spyOn(httpService, 'post')
.mockImplementationOnce(() => of(response));
myService.myMethod(input).subscribe(res => {
expect(res).toEqual(data);
});
});
});
Upvotes: 26