You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试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();
    });
  });
});

方案解释

  1. 移除useState的mock:让React正常管理组件状态,模拟真实的渲染流程。
  2. 使用waitFor等待组件更新:useEffect中的状态更新是异步触发的,waitFor会反复检查断言条件,直到条件满足或超时,确保捕获到组件重新渲染后的状态。
  3. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:44:59