如何在Jest的每个it()测试用例中为jest.mock配置不同的模拟实现
如何在Jest的每个it()测试用例中为jest.mock配置不同的模拟实现
我明白你遇到的问题了——jest.mock的执行时机很特殊,它是在代码编译阶段就运行的,不是测试运行时,所以你把它放在it()里面根本不会生效,反而会被自动提升到文件顶部,导致所有测试用例都共用同一个mock。不过别担心,有个简单可靠的方法可以实现每个测试用例使用不同的mock实现,我给你一步步拆解:
第一步:在文件顶部做基础Mock
先在测试文件最顶部,用jest.mock把你要模拟的中间件模块导出的函数替换成空的jest.fn()。这样这些函数就变成了可动态修改的mock函数,后续每个测试用例都能自定义它们的实现:
// 先在文件顶部完成基础mock,将模块导出的函数替换为可修改的jest mock函数 jest.mock('../../../middleware/awsTransferMiddleware', () => ({ transferS3Files: jest.fn(), filterPassedImage: jest.fn(), })); // 导入这个mock后的模块,方便后续在测试用例中修改实现 const { transferS3Files, filterPassedImage } = require('../../../middleware/awsTransferMiddleware'); const request = require('supertest'); const app = require('../../../path/to/your/app');
第二步:在每个测试用例中自定义Mock实现
在每个it()测试用例里,我们先清理mock的历史调用记录和旧实现,再给当前测试设置专属的mock逻辑。这样每个测试用例的mock都是独立的,不会互相影响:
第一个测试用例(模拟正常流程)
it("Should return a 200 status code if at least one image exists in the temp directory.", async () => { // 清理mock的历史调用记录和旧实现,避免之前的测试影响当前用例 transferS3Files.mockClear(); filterPassedImage.mockClear(); // 给当前测试设置专属的mock实现 transferS3Files.mockImplementation(async (req, res, next) => { // 模拟中间件正常执行,直接调用next进入下一个流程 next(); }); filterPassedImage.mockImplementation(async (req, res, next) => { // 模拟检测到有通过的图片,给req添加测试需要的属性 req.passedImages = ['test-image.jpg']; next(); }); // 执行测试请求并断言结果 const response = await request(app).get('/your-target-endpoint'); expect(response.statusCode).toBe(200); // 还可以验证mock函数是否被正确调用 expect(transferS3Files).toHaveBeenCalled(); });
第二个测试用例(模拟异常/特殊场景)
it("Should test another scenario with a different mock for the middleware.", async () => { // 用mockReset比mockClear更彻底,会清空所有调用记录和之前的实现 transferS3Files.mockReset(); filterPassedImage.mockReset(); // 设置完全不同的mock实现,比如模拟中间件出错的情况 transferS3Files.mockImplementation(async (req, res, next) => { // 模拟S3传输失败,抛出错误给错误处理中间件 next(new Error('S3 transfer failed')); }); filterPassedImage.mockImplementation(async (req, res, next) => { // 模拟没有检测到通过的图片 req.passedImages = []; next(); }); // 执行测试并断言异常场景的结果 const response = await request(app).get('/your-target-endpoint'); expect(response.statusCode).toBe(500); // 对应错误处理的状态码 expect(transferS3Files).toHaveBeenCalledTimes(1); });
补充:如果是默认导出的中间件怎么办?
如果你的中间件是默认导出的单个函数,写法稍微调整一下:
// 顶部mock默认导出的函数 jest.mock('../../../middleware/someDefaultMiddleware', () => jest.fn()); // 导入默认导出的mock函数 const someDefaultMiddleware = require('../../../middleware/someDefaultMiddleware'); // 在测试用例中修改实现 it("Tests default middleware with custom mock", async () => { someDefaultMiddleware.mockClear(); someDefaultMiddleware.mockImplementation((req, res, next) => { // 自定义逻辑 req.customData = 'test'; next(); }); // 后续测试逻辑... });
这个方法的核心是:先通过顶部的jest.mock把模块替换成可修改的mock函数,然后在运行时(每个测试用例里)动态修改它们的实现,同时用mockClear/mockReset保证测试隔离性,完美解决你需要每个测试用例用不同mock的需求。
备注:内容来源于stack exchange,提问作者Khaled Ramadan
相关产品推荐
相关产品推荐

