【问题标题】:How to test react component with axios request in useEffect?如何在useEffect中使用axios请求测试反应组件?
【发布时间】: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>
  );
};

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

【问题讨论】:

    标签: reactjs typescript axios jestjs react-testing-library


    【解决方案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 模拟。
      猜你喜欢
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      • 2021-12-25
      • 1970-01-01
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多