解决在React Testing Library中的act()警告

huangapple go评论69阅读模式
英文:

Solving act() warnings in React Testing Library

问题

I am trying to test my components and the tests are passing, however I keep getting act warnings about pieces of state that are updating in my terminal:
"警告:在测试中更新 Bird 时未包装在 act(...) 中。"
The warning is referring to the const handleRemoveBird = () => { setShowModal(true); }; state update.
该警告指的是 const handleRemoveBird = () => { setShowModal(true); }; 的状态更新。

Is this something so do with the state being linked to the Modal component and it's updating outside of the test? If that's the case (or not), what do I need to await/waitFor to fix the act warning?
这是否与状态与 Modal 组件相关,且在测试外部进行更新有关?如果是这种情况(或不是),我需要等待/等待什么来解决 act 警告?

My test:
我的测试代码:

import { screen, waitFor } from "@testing-library/react";
import user from "@testing-library/user-event";
import { renderWithProviders } from "../utils/utils-for-tests";
import Bird from "../components/Bird";

const modalContainerMock = document.createElement("div");
modalContainerMock.setAttribute("class", "modal-container");
document.body.appendChild(modalContainerMock);

const birdImgMock = jest.mock("../components/BirdImg", () => {
  return () => {
    return "Bird Img Component";
  };
});

const renderComponent = (bird) => {
  renderWithProviders(<Bird bird={bird} />);
};

const bird = {
  id: "1",
  name: "blue tit",
  number: "5",
};

test("shows modal before removal", async () => {
  renderComponent(bird);
  const removeButton = await screen.findByTestId(`removeButton-${bird.id}`);
  expect(removeButton).toBeInTheDocument();

  user.click(removeButton);

  await waitFor(() => {
    expect(screen.getByTestId("modal")).toBeInTheDocument();
  });
});
...

My component:
我的组件代码:

import { useState } from "react";
import { RxCross2 } from "react-icons/rx";
import { useRemoveBirdMutation } from "../store";
import Panel from "./Panel";
import Button from "./Button";
import Modal from "./Modal";
import Sightings from "./Sightings";
import BirdImg from "./BirdImg";

function Bird({ bird }) {
  const [removeBird, removeBirdResults] = useRemoveBirdMutation();
  const [showModal, setShowModal] = useState(false);

  const handleRemoveBird = () => {
    setShowModal(true);
  };
  const handleClose = () => {
    setShowModal(false);
  };
  const handleDelete = () => {
    removeBird(bird);
    setShowModal(false);
  };

  const actionBar = (
    <>
      <Button primary onClick={handleClose}>
        Keep
      </Button>

      <Button secondary onClick={handleDelete}>
        Delete
      </Button>
    </>
  );

  const modal = (
    <Modal actionBar={actionBar} onClose={handleClose}>
      <p>Are you sure you want to delete this bird?</p>
    </Modal>
  );
  return (
    <>
      <Panel
        primary
        data-testid="bird"
        key={bird.id}
        className="grid grid-cols-5 md:gap-5"
      >
        <div className="col-span-2">
          <BirdImg name={bird.name} />
        </div>

        <div className="col-span-2 grid content-around -ml-4 md:-ml-8">
          <h4 className="capitalize text-lg font-bold md:text-3xl ">
            {bird.name}
          </h4>
          <div>
            <Sightings bird={bird} />
          </div>
        </div>
        <div className="col-span-1 justify-self-end">
          <Button
            onClick={handleRemoveBird}
            remove
            loading={removeBirdResults.isLoading}
            data-testid={`removeButton-${bird.id}`}
          >
            <RxCross2 />
          </Button>
        </div>
      </Panel>
      {showModal && modal}
    </>
  );
}
export default Bird;
英文:

I am trying to test my components and the tests are passing, however I keep getting act warnings about pieces of state that are updating in my terminal:
" Warning: An update to Bird inside a test was not wrapped in act(...). "

The warning is referring to the const handleRemoveBird = () =&gt; {
setShowModal(true);
};
state update.

Is this something so do with the state being linked to the Modal component and it's updating outside of the test? If that's the case (or not), what do I need to await/waitFor to fix the act warning?

My test:

import { screen, waitFor } from &quot;@testing-library/react&quot;;
import user from &quot;@testing-library/user-event&quot;;
import { renderWithProviders } from &quot;../utils/utils-for-tests&quot;;
import Bird from &quot;../components/Bird&quot;;
const modalContainerMock = document.createElement(&quot;div&quot;);
modalContainerMock.setAttribute(&quot;class&quot;, &quot;modal-container&quot;);
document.body.appendChild(modalContainerMock);
const birdImgMock = jest.mock(&quot;../../components/BirdImg&quot;, () =&gt; {
return () =&gt; {
return &quot;Bird Img Component&quot;;
};
});
const renderComponent = (bird) =&gt; {
renderWithProviders(&lt;Bird bird={bird} /&gt;);
};
const bird = {
id: &quot;1&quot;,
name: &quot;blue tit&quot;,
number: &quot;5&quot;,
};
test(&quot;shows modal before removal&quot;, async () =&gt; {
renderComponent(bird);
const removeButton = await screen.findByTestId(`removeButton-${bird.id}`);
expect(removeButton).toBeInTheDocument();
user.click(removeButton);
await waitFor(() =&gt; {
expect(screen.getByTestId(&quot;modal&quot;)).toBeInTheDocument();
});
});
...

My component:

import { useState } from &quot;react&quot;;
import { RxCross2 } from &quot;react-icons/rx&quot;;
import { useRemoveBirdMutation } from &quot;../store&quot;;
import Panel from &quot;./Panel&quot;;
import Button from &quot;./Button&quot;;
import Modal from &quot;./Modal&quot;;
import Sightings from &quot;./Sightings&quot;;
import BirdImg from &quot;./BirdImg&quot;;
function Bird({ bird }) {
const [removeBird, removeBirdResults] = useRemoveBirdMutation();
const [showModal, setShowModal] = useState(false);
const handleRemoveBird = () =&gt; {
setShowModal(true);
};
const handleClose = () =&gt; {
setShowModal(false);
};
const handleDelete = () =&gt; {
removeBird(bird);
setShowModal(false);
};
const actionBar = (
&lt;&gt;
&lt;Button primary onClick={handleClose}&gt;
Keep
&lt;/Button&gt;
&lt;Button secondary onClick={handleDelete}&gt;
Delete
&lt;/Button&gt;
&lt;/&gt;
);
const modal = (
&lt;Modal actionBar={actionBar} onClose={handleClose}&gt;
&lt;p&gt;Are you sure you want to delete this bird?&lt;/p&gt;
&lt;/Modal&gt;
);
return (
&lt;&gt;
&lt;Panel
primary
data-testid=&quot;bird&quot;
key={bird.id}
className=&quot;grid grid-cols-5 md:gap-5&quot;
&gt;
&lt;div className=&quot;col-span-2&quot;&gt;
&lt;BirdImg name={bird.name} /&gt;
&lt;/div&gt;
&lt;div className=&quot;col-span-2 grid content-around -ml-4 md:-ml-8&quot;&gt;
&lt;h4 className=&quot;capitalize text-lg font-bold md:text-3xl &quot;&gt;
{bird.name}
&lt;/h4&gt;
&lt;div&gt;
&lt;Sightings bird={bird} /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div className=&quot;col-span-1 justify-self-end&quot;&gt;
&lt;Button
onClick={handleRemoveBird}
remove
loading={removeBirdResults.isLoading}
data-testid={`removeButton-${bird.id}`}
&gt;
&lt;RxCross2 /&gt;
&lt;/Button&gt;
&lt;/div&gt;
&lt;/Panel&gt;
{showModal &amp;&amp; modal}
&lt;/&gt;
);
}
export default Bird;

答案1

得分: 2

一个适合我的解决方案是,你可能需要将点击事件包裹在waitFor内。

test("shows modal before removal", async () => {
  renderComponent(bird);
  const removeButton = await screen.findByTestId(`removeButton-${bird.id}`);
  expect(removeButton).toBeInTheDocument();

  await waitFor(async () => {
    await user.click(removeButton);
  });

  expect(screen.getByTestId("modal")).toBeInTheDocument();
});
英文:

one solution that works for me, you probably need to wrap the click event inside waitFor.

test(&quot;shows modal before removal&quot;, async () =&gt; {
renderComponent(bird);
const removeButton = await screen.findByTestId(`removeButton-${bird.id}`);
expect(removeButton).toBeInTheDocument();
await waitFor(async () =&gt; {
await user.click(removeButton);
});
expect(screen.getByTestId(&quot;modal&quot;)).toBeInTheDocument();
});

huangapple
  • 本文由 发表于 2023年6月26日 03:01:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76552001.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定