如何用Jest替换Node.js单元测试中的变量值?
解决Jest测试Node.js路由时模拟数据库查询假数据的问题
我来帮你搞定这个问题!你想要在不替换整个router.get处理函数的前提下,模拟数据库查询返回的假数据,其实不用纠结"替换变量值"——我们可以通过Mock数据库模块导出的dbConnPoolPromise或者它返回的Pool对象的方法来实现,这样路由代码里的数据库调用会自动拿到我们预设的假数据,同时还能完整测试响应逻辑。
方法一:使用jest.mock完整Mock数据库模块
这种方法适合你想长期复用Mock逻辑的场景,直接让路由文件导入的数据库模块变成我们的Mock版本:
// 测试文件顶部引入依赖 const router = require('../routes/your-router-file.js'); // 替换成你的路由文件路径 const request = require('supertest'); const express = require('express'); // 初始化Express应用 const app = express(); app.use(express.json()); app.use('/', router); // Mock数据库模块 jest.mock('../database/db.js', () => { // 定义假的query方法,返回预设的假数据 const mockQuery = jest.fn().mockResolvedValue({ recordset: [{ id: 1, username: 'MockUser', email: 'mock@example.com' }] // 你的自定义假数据 }); // 假的request方法,返回带query的对象 const mockRequest = jest.fn().mockReturnValue({ query: mockQuery }); // 假的Pool对象,返回假的request const mockPool = { request: mockRequest }; return { // 保留真实的sql对象(如果路由里用到了sql的其他方法) sql: jest.requireActual('../database/db.js').sql, // 让dbConnPoolPromise返回假的Pool dbConnPoolPromise: Promise.resolve(mockPool), // 保留buildSelect的真实实现(如果需要的话) buildSelect: jest.requireActual('../database/db.js').buildSelect }; }); describe('GET / 路由测试', () => { it('应该返回200状态码和预设的假数据', async () => { // 发起请求 const response = await request(app).get('/'); // 断言响应结果 expect(response.statusCode).toBe(200); expect(response.body).toEqual({ id: 1, username: 'MockUser', email: 'mock@example.com' }); // 还可以断言数据库查询方法是否被正确调用 const { request: mockRequest } = await require('../database/db.js').dbConnPoolPromise; const mockQuery = mockRequest().query; expect(mockQuery).toHaveBeenCalledWith(expect.any(String)); // 或者精确匹配SQL_SELECT_ALL }); });
方法二:使用jest.spyOn临时Mock dbConnPoolPromise
如果只是在单个测试用例里临时Mock,用spyOn更灵活:
const router = require('../routes/your-router-file.js'); const request = require('supertest'); const express = require('express'); const dbModule = require('../database/db.js'); const app = express(); app.use(express.json()); app.use('/', router); describe('GET / 路由测试', () => { it('应该返回200状态码和预设的假数据', async () => { // 构建假的数据库调用链 const mockQuery = jest.fn().mockResolvedValue({ recordset: [{ id: 2, title: 'Mock Post', content: 'Mock Content' }] }); const mockRequest = jest.fn().mockReturnValue({ query: mockQuery }); const mockPool = { request: mockRequest }; // 替换dbModule导出的dbConnPoolPromise jest.spyOn(dbModule, 'dbConnPoolPromise').mockResolvedValue(mockPool); // 发起请求并断言 const response = await request(app).get('/'); expect(response.statusCode).toBe(200); expect(response.body).toEqual({ id: 2, title: 'Mock Post', content: 'Mock Content' }); expect(mockQuery).toHaveBeenCalledWith(expect.any(String)); }); });
为什么直接替换变量行不通?
你之前尝试直接替换变量没成功,是因为Node.js的模块缓存机制:路由文件在导入dbConnPoolPromise时,已经把这个变量缓存到了自己的模块作用域里,测试文件里修改自己的变量引用,不会影响路由文件里的那个变量。所以必须通过Mock模块导出的方式,让路由文件拿到的是我们的假对象。
这样处理后,路由代码里的const pool = await dbConnPoolPromise会拿到我们的假Pool,pool.request().query()会返回预设的假result,完全不用修改路由代码,就能测试res.status(200)、res.json()这些响应逻辑是否正确。
内容的提问来源于stack exchange,提问作者micheal
相关产品推荐
相关产品推荐

