[英]react-testing-library mocking axios.create({}) instance
我想用react-testing-library测试我的 api 我从一个名为 apiClient.ts 的文件中导出由 axios.create 创建的实例
import axios from 'axios' const apiClient = axios.create({ baseURL: process.env.REACT_APP_API_URL, responseType: 'json', headers: { 'Content-Type': 'application/json', }, }) export default apiClient
然后在我的 users.ts fetchUsersApi 中使用我从 apiClient 获取的 axios 实例
import apiClient from './apiClient' export interface ITrader { id: number name: string username: string email: string address: any phone: string website: string company: any } export const fetchTradersApi = async (): Promise<ITrader[]> => { const response = await apiClient.get<ITrader[]>('/users') return response.data }
我创建了一个mocks文件夹并在其中添加了 axios.ts
export default { get: jest.fn(() => Promise.resolve({ data: {} })), }
我的 users.spec.tsx 看起来像:
import { cleanup } from '@testing-library/react' import axiosMock from 'axios' import { fetchTradersApi } from './traders' jest.mock('axios') describe.only('fetchTradersApi', () => { afterEach(cleanup) it('Calls axios and returns traders', async () => { axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: ['Jo Smith'], }) ) const traders = await fetchTradersApi() expect(traders).toBe([{ name: 'Jo Smith' }]) expect(axiosMock.get).toHaveBeenCalledTimes(1) expect(axiosMock.get).toHaveBeenCalledWith(`${process.env.REACT_APP_API_URL}/users`) }) })
我运行我的测试,我得到:测试套件无法运行
TypeError: _axios.default.create is not a function
1 | import axios from 'axios'
2 |
> 3 | const apiClient = axios.create({
请通过创建一个适当的 axios 模拟来帮助我解决这个问题,该模拟可以提前与 react-testing-library、Tnx 一起使用。
花了一整天后,我找到了解决与我遇到的完全相同的问题的方法。 我遇到的问题与 JEST、Node 和 Typescript 组合有关。 让我简要介绍一下在其中起作用的文件:
axios-instance.ts
import axios, { AxiosInstance } from "axios";
const axiosInstance: AxiosInstance = axios.create({
baseURL: `https://example-path/products/`,
headers: {
'Content-Type': 'application/json'
}
});
export default axiosInstance;
api.ts
"use strict";
import {Request, Response, RequestHandler, NextFunction} from "express";
import axiosInstance from "./axios-instance";
/**
* POST /:productId
* Save product by productId
*/
export const save: RequestHandler = async (req: Request, res: Response, next: NextFunction) => {
try {
const response = await axiosInstance.post(`${req.params.id}/save`, req.body);
res.status(response.status).json(response.data);
} catch (error) {
res.status(error.response.status).json(error.response.data);
}
};
api.spec.ts
import { save } from "./api";
import axiosInstance from "./axios-instance";
describe.only("controller", () => {
describe("test save", () => {
let mockPost: jest.SpyInstance;
beforeEach(() => {
mockPost = jest.spyOn(axiosInstance, 'post');
});
afterEach(() => {
jest.clearAllMocks();
});
it("should save data if resolved [200]", async () => {
const req: any = {
params: {
id: 5006
},
body: {
"productName": "ABC",
"productType": "Food",
"productPrice": "1000"
}
};
const res: any = {
status: () => {
return {
json: jest.fn()
}
},
};
const result = {
status: 200,
data: {
"message": "Product saved"
}
};
mockPost.mockImplementation(() => Promise.resolve(result));
await save(req, res, jest.fn);
expect(mockPost).toHaveBeenCalled();
expect(mockPost.mock.calls.length).toEqual(1);
const mockResult = await mockPost.mock.results[0].value;
expect(mockResult).toStrictEqual(result);
});
it("should not save data if rejected [500]", async () => {
const req: any = {
params: {
id: 5006
},
body: {}
};
const res: any = {
status: () => {
return {
json: jest.fn()
}
},
};
const result = {
response: {
status: 500,
data: {
"message": "Product is not supplied"
}
}
};
mockPost.mockImplementation(() => Promise.reject(result));
await save(req, res, jest.fn);
expect(mockPost).toHaveBeenCalled();
const calls = mockPost.mock.calls.length;
expect(calls).toEqual(1);
});
});
});
对于发布的要求,我们必须模拟“axiosInstance”而不是库中的实际“axios”对象,因为我们正在从 axiosInstance 进行调用。
在规范文件中,我们导入了 axiosInstance 而不是实际的 axios
import axiosInstance from "./axios-instance";
然后我们为 post 方法创建了一个 spy(get/post/put 任何你可以 spy 的东西)
let mockPost: jest.SpyInstance;
在每个之前初始化,以便每个测试用例都有一个新的间谍开始,并且在每个之后都需要清除模拟。
beforeEach(() => {
mockPost = jest.spyOn(axiosInstance, 'post');
});
afterEach(() => {
jest.clearAllMocks();
});
模拟实现解决/拒绝
mockPost.mockImplementation(() => Promise.resolve(result));
mockPost.mockImplementation(() => Promise.reject(result));
然后调用实际方法
await save(req, res, jest.fn);
检查预期结果
expect(mockPost).toHaveBeenCalled();
expect(mockPost.mock.calls.length).toEqual(1);
const mockResult = await mockPost.mock.results[0].value;
expect(mockResult).toStrictEqual(result);
希望它有所帮助,您可以将解决方案与您的问题联系起来。 谢谢
也许注册我的答案为时已晚,但它可以帮助其他人。
在这种情况下发生的事情是上下文。 您的 apiClient 函数在另一个上下文中运行,因此其中一种方法是模拟您的 apiClient 而不是 Axios 库。
...
import apiClient from 'path-to-your-apiClient';
jest.mock('path-to-your-apiClient');
const mockedApi = apiClient as jest.Mocked<typeof apiClient>;
现在,让我们对api.spec.ts进行一些更改:
import { save } from "./api";
import axiosInstance from "./axios-instance";
import apiClient from 'path-to-your-apiClient';
jest.mock('path-to-your-apiClient');
const mockedApi = apiClient as jest.Mocked<typeof apiClient>;
describe.only("controller", () => {
describe("test save", () => {
beforeEach(() => {
jest.clearAllMocks();
mockedApi.post.mockeResolvedValue({ your-defalt-value }) // if you need
})
it("should save data if resolved [200]", async () => {
const req: any = {
params: {
id: 5006
},
body: {
"productName": "ABC",
"productType": "Food",
"productPrice": "1000"
}
};
const res: any = {
status: () => {
return {
json: jest.fn()
}
},
};
const result = {
status: 200,
data: {
"message": "Product saved"
}
};
mockedApi.post.mockResolvedValue(result);
await save(req, res, jest.fn);
expect(mockedApi.post).toHaveBeenCalled();
expect(mockedApi.post).toHaveBeenCalledTimes(1);
... // add may assertions as you want
});
....
});
});
希望这段代码可以帮助其他人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.