Mock MongoDB连接后测试用例全部失败,请求技术协助
问题排查:MongoDB DBClient测试用例全部失败
问题背景
封装了用于MongoDB操作的DBClient类,使用Sinon框架Mock连接功能编写测试用例,但运行时两个测试用例均失败,断言结果不符合预期,怀疑Mock连接环节存在问题。
待测试的DBClient类
import mongodb from 'mongodb'; import dotenv from 'dotenv'; // Configure dotenv dotenv.config(); // eslint-disable-next-line no-unused-vars class DBClient { /** * Creates a new DBclient instance. */ constructor() { const host = process.env.DB_HOST || 'localhost'; const port = process.env.DB_PORT || 27017; const database = process.env.DB_DATABASE || 'files_manager'; const dbUri = `mongodb://${host}:${port}/${database}`; this.client = new mongodb.MongoClient(dbUri, { useNewUrlParser: true, useUnifiedTopology: true, }); this.isClientConnected = false; this.client.connect((err) => { if (err) { console.error('Error encounted while connecting to MongoDB', err); } else { this.isClientConnected = true; console.log('Connected to MongoDB'); } }); } /** * check the status of the connection to MongoDB * @returns {boolean} */ isAlive() { return this.isClientConnected; } } const dbClient = new DBClient(); module.exports = dbClient;
测试代码
const { expect } = require('chai'); const sinon = require('sinon'); const mongodb = require('mongodb'); const dbClient = require('../utils/db'); describe('DBClient', function() { afterEach(function() { // Automatically restore all stubs after each test sinon.restore(); }); it('should initialize and connect successfully', function(done) { const connectStub = sinon.stub(mongodb.MongoClient.prototype, 'connect').callsFake(function(cb) { cb(null); // No Error }); // Wait for the next tick to allow the callback to execute process.nextTick(() => { expect(dbClient.isAlive()).to.be.true; expect(connectStub.calledOnce).to.be.true; done(); }); }); it('should fail to connect and isAlive returns false', function(done) { // Stub the connect method to simulate a connection failure const connectStub = sinon.stub(mongodb.MongoClient.prototype, 'connect').callsFake(function(cb) { cb(new Error('Failed to connect')); }); process.nextTick(() => { expect(dbClient.isAlive()).to.be.false; expect(connectStub.calledOnce).to.be.true(); done(); }); }); });
测试报错信息
DBClient 1) should initialize and connect successfully 2) should fail to connect and isAlive returns false 0 passing (20ms) 2 failing 1) DBClient should initialize and connect successfully: Uncaught AssertionError: expected false to be true + expected - actual -false +true at /root/alx-files_manager/test/connectToMongoDb.test.js:18:39 at processTicksAndRejections (node:internal/process/task_queues:77:11) 2) DBClient should fail to connect and isAlive returns false: Uncaught AssertionError: expected false to be true + expected - actual -false +true at /root/alx-files_manager/test/connectToMongoDb.test.js:32:43 at processTicksAndRejections (node:internal/process/task_queues:77:11)
问题根源与修复方案
核心问题
测试文件中,require('../utils/db')会立即执行DBClient的构造函数创建实例,此时Sinon的Stub还未生效,实际的MongoClient.connect已经被调用,后续的Stub对已创建的client实例没有作用,导致断言失败。另外第二个测试用例中,expect(connectStub.calledOnce).to.be.true();多了括号,属于语法错误。
修复后的测试代码
const { expect } = require('chai'); const sinon = require('sinon'); const mongodb = require('mongodb'); describe('DBClient', function() { let dbClient; afterEach(function() { sinon.restore(); }); it('should initialize and connect successfully', function(done) { // 先Stub MongoClient的connect方法 const connectStub = sinon.stub(mongodb.MongoClient.prototype, 'connect').callsFake(function(cb) { cb(null); // 模拟成功连接 }); // 清除模块缓存后重新导入,确保实例使用Stub后的方法 delete require.cache[require.resolve('../utils/db')]; dbClient = require('../utils/db'); process.nextTick(() => { expect(dbClient.isAlive()).to.be.true; expect(connectStub.calledOnce).to.be.true; done(); }); }); it('should fail to connect and isAlive returns false', function(done) { // Stub connect方法模拟连接失败 const connectStub = sinon.stub(mongodb.MongoClient.prototype, 'connect').callsFake(function(cb) { cb(new Error('Failed to connect')); }); // 清除缓存后重新导入实例 delete require.cache[require.resolve('../utils/db')]; dbClient = require('../utils/db'); process.nextTick(() => { expect(dbClient.isAlive()).to.be.false; expect(connectStub.calledOnce).to.be.true; // 修正语法错误,去掉多余括号 done(); }); }); });
关键修复点
- 延迟导入实例:每个测试用例中先完成Sinon Stub,再通过
delete require.cache清除模块缓存,重新导入dbClient,确保实例创建时使用Stub后的MongoClient方法。 - 修正断言语法:第二个测试中
to.be.true()改为to.be.true,避免调用布尔值导致的错误。 - 测试隔离:每个测试用例独立创建实例,避免测试间的状态污染。
内容的提问来源于stack exchange,提问作者rungene
相关产品推荐
相关产品推荐

