我正在调用 Nestjs 服务中的 API,如下所示,
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));
}
}
如何在不调用实际 API 的情况下模拟/监视对 this.httpClient.post() 的调用以返回响应?
describe('myMethod', () => {
it('should return the value', async () => {
const input = {
code: 'value',
};
const result = ['test'];
// spyOn?
expect(await myService.myMethod(input)).toBe(result);
});
});
使用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);
});
});
});
模拟 http 服务的一个很好的替代方法是在providers数组中声明它,如下所示。
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);
});
通过在测试模块中提供 HttpService 而不是使用间谍,可以确保 HttpModule 不会被导入或使用,并减少测试代码对其他服务的依赖。
我有一个方法正在使用我的模拟后调用的结果,所以我最终得到了这个
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);
});
});
所以通过返回这个“of()”你甚至可以通过管道传输它的结果
正如@Diaboloxx提到的,你应该通过测试中的
HttpService
设置来模拟你的providers
。
理想情况下,您应该提供并模拟您依赖注入到构造函数中的任何内容。这样您的测试就可以与给定的文件隔离,在这种情况下可以帮助防止发出真正的请求。
我喜欢使用库中的
mockDeep
:jest-mock-extended
来处理对提供者的模拟,以查看对它们调用了什么,以及模拟返回值。这也很好,因为 mockDeep
将强制类型安全来验证您是否正在模拟有效的返回数据。
旁注:测试您的依赖项是否按照您期望的方式调用是一个很好的做法。
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',
},
}),
)
})
})
})
如果您使用的是axios
确保在 packakge.json 添加
"moduleNameMapper": {
"^@shared/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
"^@app/shared(|/.*)$": "<rootDir>/libs/shared/src/$1",
"^axios$": "<rootDir>/node_modules/axios/dist/axios.min.js"
}