如何用Jest为Express API测试Mock SQLite导出常量database?
解决Jest无法Mock导出的const数据库实例问题
针对你遇到的问题——无法Mockdatabase.mjs中导出的const database常量,导致测试无法使用临时SQLite数据库副本,以下是几种可行的解决方案:
方案1:重构数据库模块,增加可配置入口
这是最稳妥的长期方案,让数据库连接支持动态路径注入,同时兼容原有代码:
修改database.mjs
import sqlite3 from 'sqlite3'; // 用函数封装数据库实例创建逻辑,支持自定义路径 let _dbInstance = null; export function getDatabase(dbPath = './db.sqlite') { if (!_dbInstance) { _dbInstance = new sqlite3.Database(dbPath); } return _dbInstance; } // 保留原有导出,确保业务代码无需修改 export const database = getDatabase();
测试文件中Mock数据库连接
import { getDatabase } from '../database.mjs'; import sqlite3 from 'sqlite3'; import fs from 'fs/promises'; import path from 'path'; import supertest from 'supertest'; // Mock getDatabase函数 jest.mock('../database.mjs', () => { const original = jest.requireActual('../database.mjs'); return { ...original, getDatabase: jest.fn(), }; }); describe('API路由测试', () => { let tempDbPath; let mockDb; let request; beforeEach(async () => { // 创建临时数据库副本 tempDbPath = path.join(__dirname, 'temp', 'test.db'); await fs.mkdir(path.dirname(tempDbPath), { recursive: true }); await fs.copyFile('./db.sqlite', tempDbPath); // 初始化临时数据库连接 mockDb = new sqlite3.Database(tempDbPath); getDatabase.mockReturnValue(mockDb); // 重置模块缓存,确保路由使用Mock后的数据库 jest.resetModules(); const app = require('../app.mjs').default; request = supertest(app); }); afterEach(async () => { // 关闭数据库连接并清理临时文件 await new Promise(resolve => mockDb.close(resolve)); await fs.unlink(tempDbPath); jest.clearAllMocks(); }); test('POST /users 应成功创建用户', async () => { const response = await request.post('/users').send({ name: 'Test User' }); expect(response.statusCode).toBe(201); // 可直接查询临时数据库验证结果 }); });
方案2:无需重构,用jest.doMock动态Mock模块
如果不想修改原有数据库模块,可以在测试前动态Mock整个模块的导出,强制使用临时数据库:
import fs from 'fs/promises'; import path from 'path'; import supertest from 'supertest'; describe('API路由测试', () => { let tempDbPath; beforeEach(async () => { // 创建临时数据库副本 tempDbPath = path.join(__dirname, 'temp', 'test.db'); await fs.mkdir(path.dirname(tempDbPath), { recursive: true }); await fs.copyFile('./db.sqlite', tempDbPath); // 动态Mock database模块,返回连接到临时文件的实例 jest.doMock('../database.mjs', () => { const sqlite3 = jest.requireActual('sqlite3'); return { database: new sqlite3.Database(tempDbPath), }; }); }); afterEach(async () => { // 关闭数据库连接并清理 const { database } = require('../database.mjs'); await new Promise(resolve => database.close(resolve)); await fs.unlink(tempDbPath); jest.resetModules(); }); test('GET /users 应返回临时数据库中的用户', async () => { const app = require('../app.mjs').default; const response = await supertest(app).get('/users'); expect(response.body.length).toBe(/* 临时数据库中的预期数量 */); }); });
关键注意事项
- 模块缓存重置:每次测试前必须调用
jest.resetModules(),避免之前的数据库实例被复用。 - 数据库连接关闭:测试后务必关闭数据库连接,否则会导致临时文件无法删除(文件被占用)。
- ES模块配置:如果项目使用
.mjs,需在jest.config.js中开启ES模块支持:export default { testEnvironment: 'node', transform: {}, extensionsToTreatAsEsm: ['.mjs'], moduleNameMapper: { '^(\\.{1,2}/.*)\\.mjs$': '$1', }, };
内容的提问来源于stack exchange,提问作者Umberto Fontanazza
相关产品推荐
相关产品推荐

