繁体   English   中英

react-testing-library 模拟 axios.create({}) 实例

[英]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 组合有关。 让我简要介绍一下在其中起作用的文件:

  1. axios-instance.ts // 初始化 axios
  2. api.ts // api 控制器
  3. api.spec.ts // api 测试文件

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM