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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:30:58