如何测试React函数组件的条件渲染(Jest + @testing-library/react)
解决React useEffect更新状态后的条件渲染测试问题
问题核心
你遇到的问题是:测试中虽然能检测到setState被调用,但组件始终渲染初始的false分支,无法覆盖loaded=true对应的代码。原因在于你mock了React.useState,但mock的实现始终返回固定的[false, setState],即使setState被调用,组件的状态也不会实际更新,自然不会触发重新渲染显示true分支。
组件代码(MyComponent.tsx)
import React from 'react' const MyComponent = () => { const [loaded, setLoaded] = React.useState(false) React.useEffect(() => { setLoaded(true) }, []) return ( <div> {loaded ? ( <div data-testid="true-text"> true </div> ) : ( <div data-testid="false-text"> false </div> )} </div> ) } export default MyComponent
修改后的测试代码(推荐方案)
遵循React Testing Library的测试理念——测试组件行为而非内部实现,不需要mockuseState,直接等待组件状态更新后的UI变化:
/** @jest-environment jsdom */ import React from "react"; import MyComponent from "../awesome-component"; import { render, waitFor, screen } from "@testing-library/react"; describe('MyComponent', () => { test('should render false-text initially, then switch to true-text', async () => { // 渲染组件 render(<MyComponent />); // 验证初始状态:false-text存在 expect(screen.getByTestId('false-text')).toBeInTheDocument(); // 等待状态更新,验证true-text出现、false-text消失 await waitFor(() => { expect(screen.getByTestId('true-text')).toBeInTheDocument(); expect(screen.queryByTestId('false-text')).not.toBeInTheDocument(); }); // 生成更新后的快照 const { asFragment } = render(<MyComponent />); await waitFor(() => { expect(asFragment()).toMatchSnapshot(); }); }); });
方案解释
- 移除
useState的mock:让React正常管理组件状态,模拟真实的渲染流程。 - 使用
waitFor等待组件更新:useEffect中的状态更新是异步触发的,waitFor会反复检查断言条件,直到条件满足或超时,确保捕获到组件重新渲染后的状态。 - 用
screen查询元素:getByTestId会在元素存在时返回,不存在时抛出错误,适合验证元素存在;queryByTestId在元素不存在时返回null,适合验证元素消失。
不推荐的mock方案(仅作参考)
如果一定要mockuseState,需要让mock实现跟踪状态变化,但这种方式会破坏React的正常渲染逻辑,不建议使用:
/** @jest-environment jsdom */ import React from "react"; import MyComponent from "../awesome-component"; import { render, waitFor, act } from "@testing-library/react"; describe('MyComponent', () => { test('should render true-text after setState is called', async () => { let currentState = false; const setState = jest.fn((newState) => { currentState = newState; // 手动触发React的更新机制(仅作演示,实际不推荐) act(() => {}); }); jest.spyOn(React, 'useState').mockImplementation(() => [currentState, setState]); const { queryByTestId } = render(<MyComponent />); // 初始状态断言 expect(queryByTestId('false-text')).toBeInTheDocument(); // 等待setState调用后状态更新 await waitFor(() => { expect(setState).toHaveBeenCalledWith(true); // 重新渲染组件以获取更新后的状态 const { queryByTestId: updatedQuery } = render(<MyComponent />); expect(updatedQuery('true-text')).toBeInTheDocument(); }); // 恢复mock jest.spyOn(React, 'useState').mockRestore(); }); });
内容的提问来源于stack exchange,提问作者buddhiv
相关产品推荐
相关产品推荐

