Jest如何测试React函数式组件内部的函数与关联状态?
React函数组件的内部状态和自定义内部函数不属于公共实例属性,Enzyme的state()、invoke()等针对类组件设计的API无法直接调用获取对应内容。以下是两种无需触发DOM事件即可完成测试的方案:
方案1:抽离自定义Hook单独测试(推荐)
将状态逻辑抽为独立自定义Hook,无需挂载整个组件即可直接测试逻辑正确性:
// 抽离的独立Hook import { useState } from 'react'; export const useMenuToggle = (initialState = false) => { const [openMenu, setOpenMenu] = useState(initialState); const toggleMenu = () => setOpenMenu(prev => !prev); return { openMenu, toggleMenu }; };
调整原有组件引入Hook:
const Component: React.FC = () => { const { openMenu, toggleMenu } = useMenuToggle(false); return ( <div> <ChildComponent openMenu={openMenu} toggleMenu={toggleMenu} /> </div> ); }; export default Component;
使用@testing-library/react的renderHook编写测试:
import { renderHook, act } from '@testing-library/react'; import { useMenuToggle } from './useMenuToggle'; test('should toggle openMenu state correctly', () => { const { result } = renderHook(() => useMenuToggle(false)); // 校验初始状态 expect(result.current.openMenu).toBe(false); // 调用toggleMenu更新状态 act(() => { result.current.toggleMenu(); }); expect(result.current.openMenu).toBe(true); // 校验二次切换逻辑 act(() => { result.current.toggleMenu(); }); expect(result.current.openMenu).toBe(false); });
方案2:直接读取子组件Props操作(无需修改业务代码)
如果不需要调整原有组件代码,可以直接从组件树中读取传给ChildComponent的props,获取toggleMenu函数直接调用:
Enzyme版本测试代码
test('should be able to toggle open menu state', () => { const wrapper = shallow( <Provider store={store}> <MemoryRouter initialEntries={['/']}> <DefaultLayout /> </MemoryRouter> </Provider> ); // 找到子组件获取传入的props const childComponent = wrapper.find(ChildComponent); // 校验初始状态 expect(childComponent.prop('openMenu')).toBe(false); // 直接调用toggleMenu childComponent.prop('toggleMenu')(); // 校验更新后状态 expect(childComponent.prop('openMenu')).toBe(true); });
React Testing Library版本测试代码
// 先mock子组件 jest.mock('./ChildComponent', () => ({ __esModule: true, default: jest.fn(() => null) })); test('should toggle openMenu correctly', () => { render( <Provider store={store}> <MemoryRouter initialEntries={['/']}> <DefaultLayout /> </MemoryRouter> </Provider> ); // 读取传给子组件的初始props const [initialProps] = ChildComponent.mock.calls[0]; expect(initialProps.openMenu).toBe(false); // 调用toggleMenu initialProps.toggleMenu(); // 读取更新后的props校验 const [updatedProps] = ChildComponent.mock.calls[1]; expect(updatedProps.openMenu).toBe(true); });
内容的提问来源于stack exchange,提问作者Alan Yong
相关产品推荐
相关产品推荐

