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

如何在Jest中为React组件传入自定义初始状态进行测试

如何在测试React组件时覆盖初始状态并分离测试数据与组件逻辑

问题描述

我有一个React组件,想用React Testing Library结合Jest做测试。组件本身自带初始状态,但测试时希望覆盖它,传入自定义初始状态来完成多场景测试。试过一种方法没生效,后来找到一个方案,但测试数据和逻辑混在组件里,想把两者彻底分离。

原组件代码

function App() {
    const initialState = [
        {
            id: uuidv4(),
            date: new Date(),
            desc: "initial",
            amount: 0,
            type: "travel",
        },
    ];
    const [expenses, setExpenses] = useState(initialState);

    return (
        <div className={Styles.app}>
            <h1 className="p-4">Expense tracker</h1>
            <Container>
                <AddExpense setExpenses={setExpenses} expenses={expenses} />
                <FilterExpense expenses={expenses} setExpenses={setExpenses} />
                <ListExpense setExpenses={setExpenses} expenses={expenses} />
                <BreakdownExpense expenses={expenses} />
            </Container>
        </div>
    );
}

无效的测试尝试

test("custom initial state", () => {
    const initialState = [
        {
            id: 1,
            date: new Date(),
            desc: "test 1",
            amount: 0,
            type: "10",
        },
        {
            id: 2,
            date: new Date(),
            desc: "test 2",
            amount: 0,
            type: "20",
        },
        {
            id: 3,
            date: new Date(),
            desc: "test 3",
            amount: 0,
            type: "30",
        },
    ];
    const [expenses, setExpenses] = React.useState(initialState);
    render(<App expenses={expenses} />);
    expect(screen.getByText(/test 1/i)).toBeInTheDocument();
    expect(screen.getByText(/test 2/i)).toBeInTheDocument();
});

不满意的解决方案(测试数据混入组件)

修改后的组件代码:

function App({ testApp }) {
const testInitialState = [
    {
        id: 1,
        date: new Date(),
        desc: "test 1",
        amount: 0,
        type: "10",
    },
    {
        id: 2,
        date: new Date(),
        desc: "test 2",
        amount: 0,
        type: "20",
    },
    {
        id: 3,
        date: new Date(),
        desc: "test 3",
        amount: 0,
        type: "30",
    },
];

const initialState = [
    {
        id: uuidv4(),
        date: new Date(),
        desc: "initial",
        amount: 0,
        type: "initial",
    },
];

const [expenses, setExpenses] = useState(
    testApp ? testInitialState : initialState
);

return (......

对应的测试代码:

test("filter expenses buttons", () => {
    render(<App testApp={true} />);
    expect(screen.getByText(/test 1/i)).toBeInTheDocument();
    expect(screen.getByText(/test 2/i)).toBeInTheDocument();
});

推荐解决方案:通过props传入可选初始状态

核心思路是让组件接受一个可选的initialExpenses props,作为初始状态的备选值。这样既不影响组件正常使用,又能在测试时灵活传入自定义数据,完全分离测试逻辑和组件代码。

修改后的组件代码

function App({ initialExpenses }) {
    // 优先使用props传入的初始状态,没有则用默认值
    const defaultInitialState = [
        {
            id: uuidv4(),
            date: new Date(),
            desc: "initial",
            amount: 0,
            type: "travel",
        },
    ];
    const [expenses, setExpenses] = useState(initialExpenses || defaultInitialState);

    return (
        <div className={Styles.app}>
            <h1 className="p-4">Expense tracker</h1>
            <Container>
                <AddExpense setExpenses={setExpenses} expenses={expenses} />
                <FilterExpense expenses={expenses} setExpenses={setExpenses} />
                <ListExpense setExpenses={setExpenses} expenses={expenses} />
                <BreakdownExpense expenses={expenses} />
            </Container>
        </div>
    );
}

测试代码(多场景示例)

// 测试默认初始状态
test("renders default initial expense", () => {
    render(<App />);
    expect(screen.getByText(/initial/i)).toBeInTheDocument();
});

// 测试自定义初始状态
test("renders custom initial expenses", () => {
    const customInitialState = [
        {
            id: 1,
            date: new Date(),
            desc: "test 1",
            amount: 0,
            type: "10",
        },
        {
            id: 2,
            date: new Date(),
            desc: "test 2",
            amount: 0,
            type: "20",
        },
    ];
    
    render(<App initialExpenses={customInitialState} />);
    
    expect(screen.getByText(/test 1/i)).toBeInTheDocument();
    expect(screen.getByText(/test 2/i)).toBeInTheDocument();
});

// 测试空初始状态场景
test("renders empty state when no initial expenses provided", () => {
    render(<App initialExpenses={[]} />);
    // 根据空状态UI编写对应断言,比如“暂无支出”文本
    expect(screen.getByText(/暂无支出/i)).toBeInTheDocument();
});

方案优势

  • 完全分离测试与组件:测试数据仅存在于测试文件中,组件代码干净无测试相关条件判断
  • 灵活性高:支持任意自定义初始状态,轻松覆盖多场景测试
  • 兼容原有逻辑:正常使用组件时不传initialExpenses,自动使用默认初始状态,不影响生产代码

内容的提问来源于stack exchange,提问作者Ansir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 23:54:32