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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 17:07:46