英文:
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 = () => {
state update.
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?
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;
答案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("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();
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论