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

如何用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(/* 临时数据库中的预期数量 */);
  });
});

关键注意事项

  1. 模块缓存重置:每次测试前必须调用jest.resetModules(),避免之前的数据库实例被复用。
  2. 数据库连接关闭:测试后务必关闭数据库连接,否则会导致临时文件无法删除(文件被占用)。
  3. ES模块配置:如果项目使用.mjs,需在jest.config.js中开启ES模块支持:
    export default {
      testEnvironment: 'node',
      transform: {},
      extensionsToTreatAsEsm: ['.mjs'],
      moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.mjs$': '$1',
      },
    };
    

内容的提问来源于stack exchange,提问作者Umberto Fontanazza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 09:20:17