如何在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
相关产品推荐
相关产品推荐

