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

如何让Jest Mock对象仅对单个测试用例生效?

如何让Jest Mock仅对单个测试用例生效?

问题描述

我当前通过全局jest.mock Mock了quizData对象,但希望该Mock仅对第一个测试用例生效,第二个测试用例使用真实数据。现有代码导致第二个测试用例无法正常运行(因为Mock数据只有1道题,无法触发"Question: 2 / 10"的文本),尝试过unmock和mockReturnValue但都无效。

原代码如下:

import {fireEvent, render, screen} from '@testing-library/react';
import App from "./App.tsx";

jest.mock("./QuizData", () => ({
    __esModule: true,
    quizData: [{
        id: 1,
        question: 'What is the capital of France?',
        answers: ['Paris', 'London', 'Berlin', 'Madrid'],
        correctAnswer: 'Paris',
    },]
}));

describe('Test the App Component', () => {

    it('checkAnswer updates score and user answers correctly', () => {
        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const answerButton = screen.getByText("Paris");
        fireEvent.click(answerButton);
        expect(screen.getByText("Score: 100")).toBeInTheDocument();
    });

    // 此测试用例无法正常运行,因为使用了全局Mock的单条数据
    it('Test the next button', () => {
        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const nextButton = screen.getByRole('nextButton');
        fireEvent.click(nextButton);
        expect(screen.getByText("Question: 2 / 10")).toBeInTheDocument();
    })
});

解决方案

方法一:使用jest.spyOn临时Mock并自动恢复

这种方法适合直接Mock模块的导出属性,测试完成后通过jest.restoreAllMocks()恢复原数据。

修改后的代码:

import {fireEvent, render, screen} from '@testing-library/react';
import App from "./App.tsx";
// 导入整个QuizData模块,方便spyOn
import * as QuizDataModule from "./QuizData";

describe('Test the App Component', () => {
    // 每个测试用例执行后恢复所有Mock
    afterEach(() => {
        jest.restoreAllMocks();
    });

    it('checkAnswer updates score and user answers correctly', () => {
        // 临时Mock quizData的getter
        jest.spyOn(QuizDataModule, 'quizData', 'get').mockReturnValue([{
            id: 1,
            question: 'What is the capital of France?',
            answers: ['Paris', 'London', 'Berlin', 'Madrid'],
            correctAnswer: 'Paris',
        }]);

        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const answerButton = screen.getByText("Paris");
        fireEvent.click(answerButton);
        expect(screen.getByText("Score: 100")).toBeInTheDocument();
    });

    it('Test the next button', () => {
        // 这里不做Mock,直接使用真实的quizData
        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const nextButton = screen.getByRole('nextButton');
        fireEvent.click(nextButton);
        expect(screen.getByText("Question: 2 / 10")).toBeInTheDocument();
    })
});

方法二:使用jest.doMock配合动态导入

这种方法通过动态导入组件,确保Mock仅作用于当前测试用例,测试后重置模块缓存。

修改后的代码:

import {fireEvent, render, screen} from '@testing-library/react';

describe('Test the App Component', () => {
    // 每个测试用例执行后重置模块缓存,避免Mock污染
    afterEach(() => {
        jest.resetModules();
    });

    it('checkAnswer updates score and user answers correctly', async () => {
        // 仅在当前测试用例中Mock模块
        jest.doMock("./QuizData", () => ({
            __esModule: true,
            quizData: [{
                id: 1,
                question: 'What is the capital of France?',
                answers: ['Paris', 'London', 'Berlin', 'Madrid'],
                correctAnswer: 'Paris',
            }]
        }));
        // 动态导入App,确保使用Mock后的模块
        const { default: App } = await import("./App.tsx");

        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const answerButton = screen.getByText("Paris");
        fireEvent.click(answerButton);
        expect(screen.getByText("Score: 100")).toBeInTheDocument();
    });

    it('Test the next button', async () => {
        // 动态导入真实的App模块
        const { default: App } = await import("./App.tsx");

        render(<App/>);
        const startButton = screen.getByRole('startButton');
        fireEvent.click(startButton);
        const nextButton = screen.getByRole('nextButton');
        fireEvent.click(nextButton);
        expect(screen.getByText("Question: 2 / 10")).toBeInTheDocument();
    })
});

原问题失效原因

全局的jest.mock会在模块加载阶段就执行,模块会被缓存到Jest的模块系统中。后续调用unmock或mockReturnValue无法覆盖已缓存的Mock,导致所有测试用例都使用同一个Mock数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:35:37