如何用React Testing Library测试useState变化时的组件渲染?
问题
我是单元测试新手,目前在React-TypeScript项目中需对组件进行单元测试。我的应用会根据useState的value1值变化,通过条件判断向ControllerComponent传递不同props并渲染对应内容。
组件代码(app.tsx)
import ControllerComponent from './controller-component/controller-component'; export interface ControllerWidgetProps { title?: string, .... } export function ControllerWidget( props: ControllerWidgetProps ) { const [value1,setValue1]=useState(-1); ... return ( <Card> <Typography > {props.title} </Typography> <Grid container spacing={1} pt={0} pb={0} sx={{marginTop:-2,marginLeft:-2}}> <Grid item xs={12} md={2}> <Box> {(value1 == -1)? ( <ControllerComponent name="xyz" image="group 55.png" /> ) : (value1 == 1 ) ? ( <ControllerComponent name="abc" image="group 56.png" />):( value1 == 2)) ?( <ControllerComponent name="pqr" image="group 57.png"/>):(<></>)} </> ))} export default UiControllerWidget;
现有测试代码
describe('UiControllerWidget', () => { it('should render successfully', () => { const { baseElement } = render( <Provider store={store} > <UiControllerWidget title={''} /> </Provider>); expect(baseElement).toBeTruthy(); }); });
我需要测试当value1变化时,是否传递了预期props并渲染正确组件。
解决方案
方法1:模拟useState控制内部状态
组件的value1是内部状态,可通过jest.spyOn模拟React的useState方法,直接指定value1的取值,再验证渲染结果。
步骤:
- 在测试文件顶部导入依赖:
import React from 'react'; import { render, screen } from '@testing-library/react'; import { Provider } from 'react-redux'; import UiControllerWidget from './app'; import ControllerComponent from './controller-component/controller-component'; import store from './path-to-your-store';
- 模拟
ControllerComponent(方便检查props传递):
jest.mock('./controller-component/controller-component', () => jest.fn((props) => <div data-testid="controller" {...props} />) );
- 编写测试用例:
describe('UiControllerWidget', () => { beforeEach(() => { jest.clearAllMocks(); // 清除mock调用记录,保证用例独立 }); it('renders ControllerComponent with name "xyz" when value1 is -1', () => { // 模拟useState返回value1=-1 jest.spyOn(React, 'useState').mockReturnValue([-1, jest.fn()]); render( <Provider store={store}> <UiControllerWidget title="" /> </Provider> ); // 验证组件渲染且props正确 expect(ControllerComponent).toHaveBeenCalledWith( expect.objectContaining({ name: 'xyz', image: 'group 55.png' }), expect.anything() // 忽略React内部传递的ref参数 ); }); it('renders ControllerComponent with name "abc" when value1 is 1', () => { jest.spyOn(React, 'useState').mockReturnValue([1, jest.fn()]); render( <Provider store={store}> <UiControllerWidget title="" /> </Provider> ); expect(ControllerComponent).toHaveBeenCalledWith( expect.objectContaining({ name: 'abc', image: 'group 56.png' }), expect.anything() ); }); it('renders nothing when value1 is not -1/1/2', () => { jest.spyOn(React, 'useState').mockReturnValue([3, jest.fn()]); render( <Provider store={store}> <UiControllerWidget title="" /> </Provider> ); // 验证ControllerComponent未被调用 expect(ControllerComponent).not.toHaveBeenCalled(); // 或验证页面中无该组件 expect(screen.queryByTestId('controller')).not.toBeInTheDocument(); }); });
方法2:重构组件为受控模式(推荐)
将value1改为可传入的props,让组件变为受控组件,测试时直接传入不同值即可,无需模拟内部状态,代码更易维护。
步骤:
- 修改组件代码,支持传入
value1props:
export interface ControllerWidgetProps { title?: string; value1?: number; // 新增props ... } export function ControllerWidget(props: ControllerWidgetProps) { // 使用传入的value1,无则用默认值-1 const [internalValue1, setValue1] = useState(props.value1 ?? -1); // 若完全受控,可直接用props.value1:const value1 = props.value1 ?? -1; ... }
- 编写测试用例:
describe('UiControllerWidget', () => { beforeEach(() => { jest.clearAllMocks(); }); it('renders correct ControllerComponent when value1 is -1', () => { render( <Provider store={store}> <UiControllerWidget title="" value1={-1} /> </Provider> ); expect(ControllerComponent).toHaveBeenCalledWith( expect.objectContaining({ name: 'xyz', image: 'group 55.png' }), expect.anything() ); }); it('renders correct ControllerComponent when value1 is 2', () => { render( <Provider store={store}> <UiControllerWidget title="" value1={2} /> </Provider> ); expect(ControllerComponent).toHaveBeenCalledWith( expect.objectContaining({ name: 'pqr', image: 'group 57.png' }), expect.anything() ); }); });
额外场景测试:交互触发状态变化
若组件内有触发setValue1的交互(如按钮点击),可直接测试交互后的状态变化:
it('switches to "abc" component after triggering value change', () => { render( <Provider store={store}> <UiControllerWidget title="" /> </Provider> ); // 假设存在一个按钮触发setValue1(1) fireEvent.click(screen.getByText('切换到1')); expect(ControllerComponent).toHaveBeenCalledWith( expect.objectContaining({ name: 'abc' }), expect.anything() ); });
内容的提问来源于stack exchange,提问作者Vidushika Dasanayka
相关产品推荐
相关产品推荐

