使用Jest与React Testing Library测试react-toastify弹窗关闭功能遇阻
问题
我有一个含按钮的简单组件,点击按钮会打开react-toastify提供的弹窗(toast),该弹窗可通过自带的关闭按钮(X)关闭。我想要为该组件编写测试用例,验证点击关闭按钮后弹窗是否关闭,但目前无法完成这个测试。以下是我的代码:
MyComponent.js
import React, { useState } from "react"; import { toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; const MyComponent = () => { const [isModalOpen, setIsModalOpen] = useState(false); const handleOpenModal = () => { if (!isModalOpen) { toast.success("This is modal content.", { autoClose: false, closeOnClick: false, onClose: () => setIsModalOpen(false), // Update state on close }); setIsModalOpen(true); } }; return ( <div> <button onClick={handleOpenModal}>Open Modal</button> </div> ); }; export default MyComponent;
MyComponent.test.js
import React from "react"; import { render, fireEvent, waitFor, act } from "@testing-library/react"; import MyComponent from "./MyComponent"; import { ToastContainer } from "react-toastify"; // Test: Clicking close button closes modal test("clicking close button closes modal", async () => { const { queryByText, getByRole } = render( <> <MyComponent /> <ToastContainer /> </> ); const button = getByRole("button", { name: "Open Modal" }); act(() => { fireEvent.click(button); }); await waitFor(() => queryByText("This is modal content.")); const closeButton = getByRole("button", { name: "close" }); expect(closeButton).toBeInTheDocument(); act(() => { fireEvent.click(closeButton); }); expect(queryByText("This is modal content.")).toBeNull(); });
运行测试后出现问题,请问该如何修改测试代码使其正常运行?
解决方案
你的测试代码存在两个核心问题,修正后即可正常运行:
- 关闭按钮定位错误:react-toastify默认关闭按钮的
aria-label不是精确的"close",而是包含"close"的文本,需要用正则匹配来定位 - 未等待关闭后的DOM更新:关闭toast是异步操作,直接断言会导致测试在DOM还未更新时执行,结果不准确
另外,React Testing Library v12+版本中,fireEvent和waitFor已经自动处理了act的逻辑,无需手动包裹。
修改后的测试代码如下:
import React from "react"; import { render, fireEvent, waitFor } from "@testing-library/react"; import MyComponent from "./MyComponent"; import { ToastContainer } from "react-toastify"; test("clicking close button closes modal", async () => { const { queryByText, getByRole } = render( <> <MyComponent /> <ToastContainer /> </> ); // 触发打开toast的操作 const openButton = getByRole("button", { name: "Open Modal" }); fireEvent.click(openButton); // 等待toast内容渲染完成 const toastContent = await waitFor(() => queryByText("This is modal content.")); expect(toastContent).toBeInTheDocument(); // 定位关闭按钮,用正则匹配aria-label中的close文本 const closeButton = getByRole("button", { name: /close/i }); expect(closeButton).toBeInTheDocument(); // 点击关闭按钮 fireEvent.click(closeButton); // 等待toast消失后断言 await waitFor(() => { expect(queryByText("This is modal content.")).toBeNull(); }); });
额外优化点:
- 直接用
await waitFor获取toast内容,简化代码逻辑 - 正则匹配关闭按钮的名称,避免因语言或版本差异导致的定位失败
- 将关闭后的断言放在
waitFor中,确保DOM更新完成后再执行检查
内容的提问来源于stack exchange,提问作者random_coder
相关产品推荐
相关产品推荐

