如何在React集成测试中模拟复用Button组件的两种交互场景
问题描述
我有一个可复用的React Button组件,代码实现如下:
const Button = ({ name = "Submit", type = "button", onClick, onKeyPress, className, disabled = false, background, testId, ...props }: ButtonProps) => { return ( <button className={...} type={type} onClick={onClick} onKeyPress={onKeyPress} {...props} disabled={disabled} data-testid={testId ?? "button"} > {name} </button> ); }; export default Button;
该组件在TestComponent中被用作两个按钮:触发handlePrevious的「Back」按钮,和触发handleNext的「Next」按钮:
const TestComponent = () => { return ( <> {/* 其他代码 */} <Button name="Back" className={...} onClick={handlePrevious} testId="previous" /> <Button name="Next" className={...} onClick={handleNext} testId="next" /> </> ) }
目前我在TestComponent.test.tsx里只能模拟其中一种按钮的交互逻辑,代码如下:
jest.mock("@/reusable/Button", () => { return jest.fn().mockImplementation((props: ButtonProps) => { return ( <button data-testid={props.testId ?? "button"} onClick={handleNext} disabled={props.disabled} > {props.name} </button> ); }); }); describe('testing buttons', () => { {/* 这里只能测试其中一个按钮(Next)的逻辑 */} });
我需要找到方法,在同一个测试块内高效模拟并测试这两个按钮的功能场景。
解决方案
核心问题是你之前的模拟代码硬编码了onClick={handleNext},导致所有Button实例共用同一点击逻辑。只需修改模拟逻辑,透传组件传入的props,即可分别测试两个按钮的交互。
方法1:透传props的模拟方式
修改Button的模拟代码,直接使用传入的props,保留每个按钮的原始交互逻辑:
jest.mock("@/reusable/Button", () => { return jest.fn((props: ButtonProps) => { return ( <button data-testid={props.testId ?? "button"} onClick={props.onClick} // 透传传入的点击事件,而非硬编码 disabled={props.disabled} type={props.type ?? "button"} > {props.name} </button> ); }); });
之后就能通过data-testid定位按钮,分别测试交互:
describe('testing buttons', () => { it('触发handlePrevious当点击Back按钮时', () => { const mockHandlePrevious = jest.fn(); // 确保TestComponent能接收并使用该mock函数(比如通过props传递) render(<TestComponent handlePrevious={mockHandlePrevious} />); fireEvent.click(screen.getByTestId('previous')); expect(mockHandlePrevious).toHaveBeenCalled(); }); it('触发handleNext当点击Next按钮时', () => { const mockHandleNext = jest.fn(); render(<TestComponent handleNext={mockHandleNext} />); fireEvent.click(screen.getByTestId('next')); expect(mockHandleNext).toHaveBeenCalled(); }); });
方法2:Jest自动模拟(更简洁)
如果Button只是原生button的简单封装,可直接用Jest自动模拟组件,同时验证props传递和交互:
// 自动模拟Button组件 jest.mock("@/reusable/Button"); describe('testing buttons', () => { it('渲染Back按钮并触发handlePrevious', () => { const mockHandlePrevious = jest.fn(); render(<TestComponent handlePrevious={mockHandlePrevious} />); const ButtonMock = require('@/reusable/Button').default; // 验证Back按钮的props是否正确 expect(ButtonMock).toHaveBeenCalledWith( expect.objectContaining({ name: "Back", testId: "previous", onClick: mockHandlePrevious }), expect.anything() ); fireEvent.click(screen.getByTestId('previous')); expect(mockHandlePrevious).toHaveBeenCalled(); }); it('渲染Next按钮并触发handleNext', () => { const mockHandleNext = jest.fn(); render(<TestComponent handleNext={mockHandleNext} />); const ButtonMock = require('@/reusable/Button').default; expect(ButtonMock).toHaveBeenCalledWith( expect.objectContaining({ name: "Next", testId: "next", onClick: mockHandleNext }), expect.anything() ); fireEvent.click(screen.getByTestId('next')); expect(mockHandleNext).toHaveBeenCalled(); }); });
内容的提问来源于stack exchange,提问作者Joshua
相关产品推荐
相关产品推荐

