act 和 useState 的单元测试错误,在 useEffect 中进行 API 调用

Posted

技术标签:

【中文标题】act 和 useState 的单元测试错误,在 useEffect 中进行 API 调用【英文标题】:Unit testing error with act and useState, Making an API call in useEffect 【发布时间】:2021-03-08 01:12:38 【问题描述】:

我有一个反应功能组件,其中应用程序的 onload 我正在使用我已包装在 useEffect 中的 API 调用,当收到数据时我正在更新 useState 变量。 在使用 jest 和 react 测试库为以下组件编写单元测试用例时,我收到 act 错误和 useState 变量错误。 代码:

import React,  useState, useEffect  from "react";
import TextField from "@material-ui/core/TextField";
import Media from "./card";
import Alert from "@material-ui/lab/Alert";
import fetchData from "../API";
const Dashboard = () => 
  const [searchInput, setSearchInput] = useState(null);
  const [receipeNotFound, setReceipeNotFound] = useState(false);

  useEffect(() => 
    fetchData(
      `https://www.themealdb.com/api/json/v1/1/random.php`,
      randomReceipe
    );
  , []);

  const randomReceipe = (result) => 
    result.meals ? setSearchInput(result.meals[0]) : setSearchInput(null);
  ;

  const searchData = (value) => 
    if (value) 
      setSearchInput(null);
      setReceipeNotFound(false);
      fetchData(
        `https://www.themealdb.com/api/json/v1/1/search.php?s=$value`,
        searchRecepie
      );
     else 
      setSearchInput(null);
    
  ;

  const notFound = (status) => 
    setReceipeNotFound(status);
    setSearchInput(null);
  ;

  const searchRecepie = (result) => 
    result.meals ? setSearchInput(result.meals[0]) : notFound(true);
  ;

  return (
    <div>
      <TextField
        data-testid="searchInput"
        id="standard-basic"
        label="Search"
        onBlur=(event) => searchData(event.currentTarget.value)
      />
      receipeNotFound ? (
        <Alert data-testid="alert" severity="error">
          Receipe not found!
        </Alert>
      ) : null
      Boolean(searchInput) ? (
        <Media data-testid="mediaLoading" loading=false data=searchInput />
      ) : (
        <Media data-testid="Media" loading=true data= />
      )
    </div>
  );
;

export default Dashboard;

测试:

import React from "react";
import Dashboard from "./dashboard";
import ReactDOM from "react-dom";
import 
  render,
  screen,
  act,
  waitFor,
  fireEvent,
 from "@testing-library/react";
import  randomMockWithOutVideo, randomMockWithVideo  from "./API.mock";
global.fetch = jest.fn();
let container;

describe("Card ", () => 
  test("Should renders data when API request is called with meals data", async (done) => 
    jest.spyOn(global, "fetch").mockResolvedValue(
      json: jest.fn().mockResolvedValue( meals: [randomMockWithVideo] ),
    );
    const  getByTestId, container  = render(<Dashboard />);
    expect(global.fetch).toHaveBeenCalledTimes(1);
    expect(global.fetch).toHaveBeenCalledWith(
      `https://www.themealdb.com/api/json/v1/1/random.php`
    );
  );

  afterEach(() => 
    jest.restoreAllMocks();
  );
);

错误: 警告:测试中 Dashboard 的更新未包含在 act(...) 中。

  When testing, code that causes React state updates should be wrapped into act(...):

  act(() => 
    /* fire events that update state */
  );
  /* assert on the output */

19 |结果.餐? setSearchInput(result.meals[0]) : setSearchInput(null); | ^

【问题讨论】:

第 19 行抛出的错误是什么? 您使用的是什么版本的 RTL? 【参考方案1】:

此警告表明在您的组件被拆除后已对其进行了更新(通过 useState)。您可以在此处阅读 Kent C. Dodds 的完整解释:https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning/。

我会尝试这种语法来消除警告:

await act(() => 
  expect(global.fetch).toHaveBeenCalledTimes(1);
  expect(global.fetch).toHaveBeenCalledWith(`https://www.themealdb.com/api/json/v1/1/random.php`);
);

【讨论】:

以上是关于act 和 useState 的单元测试错误,在 useEffect 中进行 API 调用的主要内容,如果未能解决你的问题,请参考以下文章

警告:测试中对 App 的更新未包含在酶和钩子中的 act(...) 中

Ruby on Rails:RSpec 和 Acts As Audited(在测试时禁用acts_as_audited?)

使用玩笑的反应单元测试失败

使用钩子时 Jest/Enzyme 测试抛出错误

测试时,导致React状态更新的代码应包装为act(…)

AAA 是 Coded UI 测试的好习惯吗?