使用Mocha测试返回Promise的deferred.promise及Node.js Bot SDK代码问题
Mocha对Promise的支持非常友好,根本不需要额外的复杂配置——你只需要让测试用例直接返回Promise,Mocha就会自动等待它完成(resolve或reject),不用手动调用done回调。我给你举几个实际的例子:
假设你有一个返回deferred.promise的函数,比如用Q库实现的延迟操作:
// 示例函数:返回deferred.promise const Q = require('q'); function getDeferredData() { const deferred = Q.defer(); setTimeout(() => { // 模拟成功返回数据 deferred.resolve({ status: 'ok', data: ['room1', 'room2'] }); // 模拟失败的情况可以用deferred.reject(new Error('获取数据失败')) }, 100); return deferred.promise; }
对应的Mocha测试用例可以这么写:
const assert = require('assert'); const getDeferredData = require('./your-module'); describe('Deferred Promise 测试', () => { // 测试成功场景:直接返回Promise,用then断言结果 it('应该成功返回会议室数据', () => { return getDeferredData().then(result => { assert.strictEqual(result.status, 'ok'); assert.ok(Array.isArray(result.data)); }); }); // 用async/await写法更简洁,可读性更高 it('用async/await测试成功场景', async () => { const result = await getDeferredData(); assert.strictEqual(result.status, 'ok'); assert.deepStrictEqual(result.data, ['room1', 'room2']); }); // 测试失败场景:返回Promise并在catch里断言错误 it('应该捕获获取数据失败的错误', () => { return getFailedDeferredData().catch(error => { assert.strictEqual(error.message, '获取数据失败'); }); }); });
关键注意点:
- 不要在返回Promise的测试里同时用
done,否则可能导致测试超时或报错 - 如果测试reject的情况,一定要确保错误被catch到,不然Mocha会把它当成测试失败
- 优先用async/await写法,比嵌套then链清爽太多
先把你给出的代码片段补全(推测后面是then/catch链),方便分析:
resolveMeetingRoomFreeTime: function (session, args) { return new Promise(function (resolve, reject) { kgService.getFreeMeetingRooms( session.conversationData.floorEntityList, session.conversationData.roomEntityList, session.conversationData.meetingStartTimestamp, session.conversationData.meetingEndTimestamp, session.conversationData.meetingStartTime, session.conversationData.meetingEndTime, session.conversationData.meetingDuration ) .then(freeRooms => resolve(freeRooms)) .catch(error => reject(error)); }); }
我给你列几个最可能的问题点和排查步骤:
1. 先排查参数合法性
session.conversationData里的参数很可能是undefined、null或者类型错误,比如floorEntityList不是数组,meetingStartTimestamp不是合法的时间戳。你可以在调用前加日志打印所有参数:
resolveMeetingRoomFreeTime: function (session, args) { // 先打印参数,排查是否有异常值 console.log('传入getFreeMeetingRooms的参数:', { floorEntityList: session.conversationData.floorEntityList, roomEntityList: session.conversationData.roomEntityList, meetingStartTimestamp: session.conversationData.meetingStartTimestamp, meetingEndTimestamp: session.conversationData.meetingEndTimestamp, meetingStartTime: session.conversationData.meetingStartTime, meetingEndTime: session.conversationData.meetingEndTime, meetingDuration: session.conversationData.meetingDuration }); return new Promise(function (resolve, reject) { // 原代码... }); }
2. 去掉多余的Promise嵌套(反模式)
你现在用new Promise包裹kgService.getFreeMeetingRooms的Promise完全没必要,属于Promise嵌套反模式,反而容易隐藏错误。直接返回kgService.getFreeMeetingRooms的Promise即可,还能简化代码:
resolveMeetingRoomFreeTime: function (session, args) { return kgService.getFreeMeetingRooms( session.conversationData.floorEntityList, session.conversationData.roomEntityList, session.conversationData.meetingStartTimestamp, session.conversationData.meetingEndTimestamp, session.conversationData.meetingStartTime, session.conversationData.meetingEndTime, session.conversationData.meetingDuration ) .then(freeRooms => { // 这里可以加业务处理逻辑,比如过滤会议室 return freeRooms.filter(room => room.capacity >= 10); }) .catch(error => { // 在这里加错误日志,方便定位问题 console.error('获取空闲会议室失败:', error.stack); // 重新抛出错误让上层调用者处理 throw error; }); }
3. 单独测试kgService.getFreeMeetingRooms
问题可能出在kgService.getFreeMeetingRooms内部,比如它的参数校验严格、调用第三方接口失败、数据库查询错误等。你可以写个单独的测试用例,传入模拟的合法参数,看它是否能正常返回:
const assert = require('assert'); const kgService = require('./kgService'); describe('kgService.getFreeMeetingRooms', () => { it('传入合法参数应该返回空闲会议室列表', async () => { const mockParams = { floorEntityList: ['1F', '2F'], roomEntityList: ['大会议室', '小会议室'], meetingStartTimestamp: Date.now(), meetingEndTimestamp: Date.now() + 3600000, // 1小时后 meetingStartTime: '09:00', meetingEndTime: '10:00', meetingDuration: 60 }; const result = await kgService.getFreeMeetingRooms( mockParams.floorEntityList, mockParams.roomEntityList, mockParams.meetingStartTimestamp, mockParams.meetingEndTimestamp, mockParams.meetingStartTime, mockParams.meetingEndTime, mockParams.meetingDuration ); assert.ok(Array.isArray(result), '应该返回数组类型的结果'); assert.ok(result.length >= 0, '结果长度应该大于等于0'); }); });
4. 检查上层调用是否处理了错误
如果调用resolveMeetingRoomFreeTime的地方没有加catch,会导致Uncaught Promise Rejection,看起来像是函数内部异常。一定要确保上层调用有错误处理:
// 上层调用示例 resolveMeetingRoomFreeTime(session, args) .then(freeRooms => { session.send(`找到${freeRooms.length}个空闲会议室:${freeRooms.map(r => r.name).join(', ')}`); }) .catch(error => { session.send('抱歉,获取会议室空闲时间失败,请稍后重试'); console.error('上层捕获的错误:', error); });
内容的提问来源于stack exchange,提问作者Royjad

