如何让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
相关产品推荐
相关产品推荐

