如何为node_redis的setExp函数编写正确的Mocha测试用例?
解决node_redis异步函数Mocha测试的错误场景问题
首先得指出你当前代码和测试用例里的几个核心问题:
- 你的
setExp是异步函数,但没有对外暴露回调参数,导致测试根本无法捕获Redis操作的错误或结果; - 测试用例是同步写法,直接调用
setExp后立刻断言,完全没等待异步操作完成; - 直接连接真实Redis服务很难构造预期的错误场景,需要用Mock/Stub模拟Redis客户端的行为。
下面一步步给出修正方案:
第一步:修正cache.js的setExp函数
给函数添加回调参数,把Redis操作的错误和结果传递出去:
var redis = require('redis'); // 你原来的代码里应该有这句,补上 var redisCache = redis.createClient(port, name); redisCache.on("error", function(err) { logger.error("Error connecting to redis", err); }); // 添加callback参数,用于传递异步操作的错误/结果 exports.setExp = function(key, timeLeft, data, callback){ redisCache.set(key, JSON.stringify(data), function (err, reply) { if(err) { logger.info("This errror on set key related to node_redis"); return callback(err); // 把SET错误传递给调用者 } if(reply === 'OK') { redisCache.expire(key, timeLeft, function (err, reply) { if(err) { logger.info("This errror on expire key related to node_redis"); return callback(err); // 把EXPIRE错误传递给调用者 } if(reply === 1) { logger.info(`${key} key expire time set as ${timeLeft} successfully!`); callback(null, reply); // 成功时传递结果 } }); } }); }
第二步:编写正确的Mocha测试用例
我们用sinon(用于Stub方法)和rewire(用于访问模块私有变量)来模拟Redis客户端的行为,不需要真实Redis服务就能构造错误场景。
先安装依赖:
npm install mocha sinon rewire --save-dev
测试用例代码:
var assert = require('assert'); var rewire = require('rewire'); var sinon = require('sinon'); // 用rewire加载cache模块,方便访问私有变量redisCache var cache = rewire('../cache'); describe('cache', function () { let redisCacheStub; let setStub; let expireStub; // 每个测试前,Stub Redis客户端的set/expire方法 beforeEach(function() { redisCacheStub = cache.__get__('redisCache'); setStub = sinon.stub(redisCacheStub, 'set'); expireStub = sinon.stub(redisCacheStub, 'expire'); }); // 每个测试后恢复原方法,避免影响其他测试 afterEach(function() { setStub.restore(); expireStub.restore(); }); it('should return SET command error when key is undefined', function (done) { // 构造预期的错误对象 const testError = { command: 'SET', code: 'ERR' }; // 模拟set方法调用时返回错误 setStub.callsArgWith(2, testError, null); // 调用setExp并传入回调,在回调里断言 cache.setExp(undefined, 0, [], function(err) { assert.equal(err.command, 'SET'); done(); // 告诉Mocha异步操作完成 }); }); it('should return "ERR wrong number of arguments for \'set\' command" error', function (done) { const testError = new Error('ERR wrong number of arguments for \'set\' command'); setStub.callsArgWith(2, testError, null); cache.setExp('foo', 10, [], function(err) { assert.equal(err.message, 'ERR wrong number of arguments for \'set\' command'); done(); }); }); it('should set expire time successfully with valid arguments', function (done) { // 模拟set返回OK,expire返回1(表示成功设置过期时间) setStub.callsArgWith(2, null, 'OK'); expireStub.callsArgWith(2, null, 1); cache.setExp('test-key', 60, { content: 'test data' }, function(err, reply) { assert.isNull(err); assert.equal(reply, 1); done(); }); }); });
关键说明
- 异步函数必须暴露回调:Redis操作都是异步的,你必须通过回调(或Promise)把结果/错误传递给调用者,否则测试无法捕获异步操作的状态;
- 用Stub模拟Redis行为:直接连接真实Redis很难稳定构造错误场景,用
sinonStub Redis的方法,可以完全控制它返回的错误或结果; - 异步测试的正确姿势:Mocha测试异步代码时,必须调用
done()回调,或者返回Promise,确保测试等待异步操作完成后再执行断言。
内容的提问来源于stack exchange,提问作者Mukesh S
相关产品推荐
相关产品推荐

