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

如何在Vitest中Mock React组件的ldContext状态变量

解决React HOC内部useState状态在Testing Library中无法Mock/获取的问题

方案1:给HOC添加测试专用的覆盖入口

直接修改HOC,允许外部传入mock状态值,优先使用传入的值而非内部useState的初始值:

const withFeatureFlagProvider = (Component) => {
  return (props) => {
    // 优先使用外部传入的测试用状态,否则沿用原有逻辑
    const [ldContext, setLdContext] = useState(
      props.testOverrideLdContext ?? null
    );
    
    // 原有的业务逻辑(比如初始化LDContext的逻辑)
    // ...

    return <Component ldContext={ldContext} {...props} />;
  };
};

测试时直接传入testOverrideLdContext来控制状态:

import { render } from '@testing-library/react';
import YourWrappedComponent from './YourWrappedComponent';

it('执行ldContext存在时的逻辑', () => {
  const mockLdContext = { key: 'test-value', /* 其他LDContext字段 */ };
  
  const { getByText } = render(
    <YourWrappedComponent testOverrideLdContext={mockLdContext} />
  );
  
  // 断言该分支下的UI或行为
  expect(getByText('功能已启用')).toBeInTheDocument();
});

it('执行ldContext为null时的逻辑', () => {
  const { getByText } = render(
    <YourWrappedComponent testOverrideLdContext={null} />
  );
  
  expect(getByText('功能暂不可用')).toBeInTheDocument();
});

这种方式侵入性低,仅为测试添加入口,不影响生产代码逻辑。

方案2:Mock React的useState钩子

通过Jest直接mock useState,强制返回预设的状态值:

import React from 'react';
import { render } from '@testing-library/react';
import YourWrappedComponent from './YourWrappedComponent';

// 全局mock useState,保留React其他原有功能
jest.mock('react', () => ({
  ...jest.requireActual('react'),
  useState: jest.fn(),
}));

describe('Feature Flag组件测试', () => {
  // 每个测试后清理mock,避免污染其他用例
  afterEach(() => {
    React.useState.mockClear();
  });

  it('使用mock的ldContext状态', () => {
    const mockLdContext = { /* 你的mock数据 */ };
    // 让useState返回我们预设的状态和空的setter
    React.useState.mockReturnValue([mockLdContext, jest.fn()]);

    const { getByRole } = render(<YourWrappedComponent />);
    expect(getByRole('button', { name: '开启新功能' })).toBeInTheDocument();
  });

  it('处理ldContext为null的情况', () => {
    React.useState.mockReturnValue([null, jest.fn()]);

    const { queryByRole } = render(<YourWrappedComponent />);
    expect(queryByRole('button', { name: '开启新功能' })).not.toBeInTheDocument();
  });
});

注意:如果组件中存在多个useState,这个方法可能会影响其他状态,需要确保mock的返回值顺序匹配组件内的调用顺序。

方案3:基于用户行为而非内部状态测试(推荐)

Testing Library的核心理念是测试用户可见的行为,而非内部实现细节。你不需要直接获取ldContext,而是通过断言不同状态下的UI表现来验证逻辑:

举个例子,如果ldContext存在时显示功能按钮,不存在时显示提示文本,直接测试这些UI元素即可:

// 这里可以结合方案1的testOverrideLdContext来注入状态
it('ldContext存在时显示功能按钮', () => {
  const mockLdContext = { /* mock数据 */ };
  const { getByRole } = render(
    <YourWrappedComponent testOverrideLdContext={mockLdContext} />
  );
  expect(getByRole('button', { name: '新功能入口' })).toBeInTheDocument();
});

it('ldContext为null时显示提示文本', () => {
  const { getByText } = render(
    <YourWrappedComponent testOverrideLdContext={null} />
  );
  expect(getByText('新功能正在准备中')).toBeInTheDocument();
});

这种方式更健壮,即使后续HOC内部实现(比如把useState换成useReducer),测试用例也不需要修改。

方案4:如果使用Context传递状态,直接Mock Context Provider

如果withFeatureFlagProvider内部是通过React Context来传递ldContext的,直接在测试中用Context.Provider包裹组件,传入mock值:

// 假设你导出了对应的Context
import { LdContext } from './feature-flag-context';
import { render } from '@testing-library/react';
import YourComponent from './YourComponent';

it('使用mock的Context值', () => {
  const mockLdContext = { /* mock数据 */ };
  
  const { getByText } = render(
    <LdContext.Provider value={mockLdContext}>
      <YourComponent />
    </LdContext.Provider>
  );
  
  expect(getByText('功能已激活')).toBeInTheDocument();
});

内容的提问来源于stack exchange,提问作者SunLightGirl99

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:22:29