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

Kam*_*ran 6 mocking reactjs axios react-testing-library

我想用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
Run Code Online (Sandbox Code Playgroud)

然后在 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
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个mocks文件夹并在其中添加了axios.ts

export default {
  get: jest.fn(() => Promise.resolve({ data: {} })),
}
Run Code Online (Sandbox Code Playgroud)

我的 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`)
  })
})
Run Code Online (Sandbox Code Playgroud)

我运行测试并得到: 测试套件运行失败

TypeError: _axios.default.create is not a function

  1 | import axios from 'axios'
  2 |
> 3 | const apiClient = axios.create({
Run Code Online (Sandbox Code Playgroud)

请提前创建一个与 React-testing-library、Tnx 配合使用的适当的 axios 模拟,帮助我解决问题。

Zak*_*med 4

经过一整天的努力,我找到了与我遇到的完全相同的问题的解决方案。我遇到的问题与 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;
Run Code Online (Sandbox Code Playgroud)

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);
    }
};
Run Code Online (Sandbox Code Playgroud)

api 规范.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);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

对于发布的要求,我们必须模拟“axiosInstance”而不是库中实际的“axios”对象,因为我们正在从 axiosInstance 进行调用。

在规范文件中,我们导入了 axiosInstance 而不是实际的 axios

import axiosInstance from "./axios-instance";
Run Code Online (Sandbox Code Playgroud)

然后我们为 post 方法创建了一个间谍(获取/发布/放置任何你可以监视的内容)

let mockPost: jest.SpyInstance;
Run Code Online (Sandbox Code Playgroud)

在每个测试用例之前进行初始化,以便每个测试用例都有一个新的间谍开始,并且在每个测试用例之后还需要清除模拟。

beforeEach(() => {
    mockPost = jest.spyOn(axiosInstance, 'post');
});

afterEach(() => {
    jest.clearAllMocks();
});
Run Code Online (Sandbox Code Playgroud)

模拟实施已解决/拒绝

mockPost.mockImplementation(() => Promise.resolve(result));
mockPost.mockImplementation(() => Promise.reject(result));
Run Code Online (Sandbox Code Playgroud)

然后调用实际的方法

await save(req, res, jest.fn);
Run Code Online (Sandbox Code Playgroud)

检查预期结果

expect(mockPost).toHaveBeenCalled();
expect(mockPost.mock.calls.length).toEqual(1);
const mockResult = await mockPost.mock.results[0].value;
expect(mockResult).toStrictEqual(result);
Run Code Online (Sandbox Code Playgroud)

希望它对您有所帮助,并且您可以将解决方案与您的问题联系起来。谢谢