如何在useEffect中使用axios请求测试反应组件?

Posted

技术标签:

【中文标题】如何在useEffect中使用axios请求测试反应组件?【英文标题】:How to test react component with axios request in useEffect? 【发布时间】:2022-01-23 18:25:45 【问题描述】:

我已经在 useEffect 中根据请求对功能组件做出反应。 https://codesandbox.io/s/nifty-dew-r2p1d?file=/src/App.tsx

const App = () => 
  const [data, setData] = useState<IJoke | undefined>(undefined);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  useEffect(() => 
    axios
      .get("https://v2.jokeapi.dev/joke/Programming?type=single")
      .then((res: AxiosResponse<IJoke>) => 
        setData(res.data);
      )
      .catch((err) => console.log(err))
      .finally(() => setIsLoading(false));
  , []);

  return (
    <div className="App">
      isLoading ? (
        <h2>Loading...</h2>
      ) : (
        <div className="info">
          <div className="info__cat">
            data?.category ? `category: $data.category` : "bad category"
          </div>
          <div className="info__joke">
            data?.joke ? `joke: $data?.joke` : "bad data"
          </div>
        </div>
      )
    </div>
  );
;

如何使用测试覆盖组件?我需要在请求之前、及时和之后测试状态。在这种情况下如何模拟请求?

【问题讨论】:

【参考方案1】:

您无法通过 react-testing-library 在内部访问 statesetState。这是因为 RTL 希望您像用户一样测试您的组件。 您应该尝试重现更改状态的一组操作。 使用react-testing-libraryrender函数渲染组件,将模拟的statesetState传递给该组件。 使用axios 中的axiosMock 模拟axios。 在这种方法中,您可以将模拟值作为真实的本地状态进行检查。

import  render  from "react-testing-library";
import axiosMock from "axios";

此链接可能会有所帮助:

async-axios-react-testing-library

【讨论】:

【参考方案2】:

选项1.使用msw通过拦截网络级别的请求来模拟。

选项 2。如果您不想安装任何软件包和设置,您可以使用 jest.spyOn(object, 'method').mockResolvedValueOnce()axios.get() 方法创建模拟解析/拒绝值。

以下示例使用选项 2。

App.tsx:

import axios,  AxiosResponse  from 'axios';
import React,  useEffect, useState  from 'react';

interface IJoke 
  category: string;
  joke: string;


export const App = () => 
  const [data, setData] = useState<IJoke | undefined>(undefined);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  useEffect(() => 
    axios
      .get('https://v2.jokeapi.dev/joke/Programming?type=single')
      .then((res: AxiosResponse<IJoke>) => 
        setData(res.data);
      )
      .catch((err) => console.log(err))
      .finally(() => setIsLoading(false));
  , []);

  return (
    <div className="App">
      isLoading ? (
        <h2>Loading...</h2>
      ) : (
        <div className="info">
          <div className="info__cat">data?.category ? `category: $data.category` : 'bad category'</div>
          <div className="info__joke">data?.joke ? `joke: $data?.joke` : 'bad data'</div>
        </div>
      )
    </div>
  );
;

App.test.tsx:

import  App  from './App';
import axios,  AxiosResponse  from 'axios';
import  act, render, screen  from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';

describe('70450576', () => 
  afterEach(() => 
    jest.restoreAllMocks();
  );
  test('should render category and joke', async () => 
    const mAxiosResponse = 
      data:  category: 'smart', joke: 'sam' ,
     as AxiosResponse;
    jest.spyOn(axios, 'get').mockResolvedValueOnce(mAxiosResponse);
    render(<App />);
    expect(screen.getByText('Loading...')).toBeInTheDocument();
    expect(await screen.findByText('category: smart')).toBeInTheDocument();
    expect(await screen.findByText('joke: sam')).toBeInTheDocument();
  );
);

测试结果:

 PASS  examples/70450576/App.test.tsx (8.874 s)
  70450576
    ✓ should render category and joke (43 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   91.67 |    72.22 |      80 |   90.91 |                   
 App.tsx  |   91.67 |    72.22 |      80 |   90.91 | 19                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.393 s, estimated 10 s

【讨论】:

好的,我知道如何测试这种情况了,谢谢))但是,如果我们有自定义请求功能,比如link?如何模拟这个函数? @СлаваИванов 你从一个模块中导入一个函数,它不是一个对象,不能再使用jest.spyOn(object, 'method')。现在,您可以使用jest.mock("./app.service") 来模拟模块。 Jest 会自动将 jest.mock 调用提升到模块顶部(在任何导入之前)。 getJoke 函数被模拟,然后您可以模拟每个测试用例中已解析/拒绝的值,例如 getJoke.mockResolvedValueOnce(xxx)。此外,您可以使用 ts-jest/utils 中的 mocked 辅助函数来使 TS 类型正确。跨度> +1 用于使用 MSW。不要模拟您的请求客户端/功能的实现细节。而是在更高级别上进行 API 模拟。

以上是关于如何在useEffect中使用axios请求测试反应组件?的主要内容,如果未能解决你的问题,请参考以下文章

使用 useEffect 反应 axios 获取请求而不会导致无限循环

React - useEffect axios请求中设置的状态为空

使用 Jest Mock Ajax

无法在反应测试库中使用模拟服务人员

无法在 useEffect() 中从 AXIOS 获取数据

React / Axios 发送无限数量的请求