目前,我正在学习如何使用React进行单元测试。但是,我想用TypeScript来学习它,所以这门课程不涵盖TS发生的大多数错误。
我有一个简单的测试功能,配置了Mock Service Worker (msw):
fit("sends username, email and password to backend after clicking the button", async () => {
let requestBody;
const server = setupServer(
rest.post("/api/1.0/users", (req, res, ctx) => {
requestBody = req.body;
return res(ctx.status(200));
})
);
server.listen();
setupAll(); // Gets the elements on the page (like button)
userEvent.click(button);
await new Promise((resolve) => setTimeout(resolve, 250));
expect(requestBody).toEqual({
username: "LegacyUser",
email: "legacy@user.com",
password: "P455w0rd!",
});
});从理论上讲,这个“works”(它在测试列表中显示为Pass ),但在它之上会出现错误,例如:
console.error
Warning: An update to SignUpPage inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */因此,当我像userEvent.click(button)一样包装act(() => userEvent.click(button))时,它会一直显示这个错误消息。
单击userEvent触发onSubmit处理程序:
const formSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const { username, email, password } = formEntries;
setIsApiInProgress(true);
axios
.post("/api/1.0/users", {
username,
email,
password,
})
.then((response) => {
if (response.status >= 200 && response.status < 300) {
setIsSignUpSuccessfull(true);
// console.log("OK", response.data);
return response.data;
}
throw new Error(response.status.toString());
})
.catch(({ response }) => {
console.log("CATCH", response);
})
.finally(() => {
setIsApiInProgress(false);
});
};我在这里做错什么了?另外,是否有一种方法可以在不使用超时的情况下等待已解决的承诺?这种感觉有点烦人。
发布于 2022-02-02 04:24:00
这不是打字稿相关的问题。您需要将这一行await new Promise((resolve) => setTimeout(resolve, 250));包装在act中,即await act(() => new Promise((resolve) => setTimeout(resolve, 250)))。您可以阅读更多关于它的这里。
与你的问题最相关的是这一部分:
因此,反应的警告是告诉我们,当我们没有预料到会发生什么事情时,我们的组件发生了一些事情。因此,您应该包装您与组件在行动中进行的每一次交互,让React知道我们的组件会执行一些更新,当您不这样做时,如果有更新,React会警告我们发生了意外的更新。这有助于我们避免像上面描述的那样的bug。 幸运的是,对您和我来说,React会自动处理在React调用堆栈中运行的任何代码(就像在事件处理程序代码中单击React调用来更新组件),但是它不能处理运行在它自己的调用堆栈之外的任何代码(就像异步代码因为您正在管理的解析承诺而运行,或者如果您使用的是开玩笑的假计时器)。在这种情况下,通常需要在行动中将其包装(.)或异步行为(.)你自己。但是,有自动包装在act中的异步实用程序!
请注意,它特别提到了如何为您处理单击事件(因此,为什么包装userEvent没有解决问题)、和--如何处理您自己管理自己的承诺--(因此,我们自己包装它的原因)。
你说得对,这是一种讨厌的方式。您可以使用RTL的异步方法,这些方法在其文档中得到了很好的解释。
这是:
await new Promise((resolve) => setTimeout(resolve, 250));
expect(requestBody).toEqual({
username: "LegacyUser",
email: "legacy@user.com",
password: "P455w0rd!",
});变成这样:
await waitFor(() => {
expect(requestBody).toEqual({
username: "LegacyUser",
email: "legacy@user.com",
password: "P455w0rd!",
});
})https://stackoverflow.com/questions/70849044
复制相似问题