我有一个使用node_redis库(https://github.com/NodeRedis/node_redis)创建的Redis客户端:
var client = require('redis').createClient(6379, 'localhost');
我有一个我想测试的方法,其目的是设置并向Redis发布一个值,所以我想测试以确保根据调用或不调用set
和publish
方法我的期望。棘手的是我希望这个测试能够在不需要启动Redis服务器实例的情况下工作,因此我不能创建客户端,因为如果无法检测到Redis,它将会抛出错误。因此,我需要存根createClient()
方法。
示例方法:
// require('redis').createClient(port, ip) is called once and the 'client' object is used globally in my module.
module.exports.updateRedis = function (key, oldVal, newVal) {
if (oldVal != newVal) {
client.set(key, newVal);
client.publish(key + "/notify", newVal);
}
};
我已经尝试了几种方法来测试是否使用预期的键和值调用set和publish,但是没有成功。如果我试图窥探这些方法,我可以告诉我的方法是通过运行调试器来调用的,但是对于我来说,callOnce并没有被标记为真。如果我将createClient
方法存根以返回假客户端,例如:
{
set: function () { return 'OK'; },
publish: function () { return 1; }
}
测试中的方法似乎没有使用虚假客户端。
现在,我的测试看起来像这样:
var key, newVal, oldVal, client, redis;
before(function () {
key = 'key';
newVal = 'value';
oldVal = 'different-value';
client = {
set: function () { return 'OK'; },
publish: function () { return 1; }
}
redis = require('redis');
sinon.stub(redis, 'createClient').returns(client);
sinon.spy(client, 'set');
sinon.spy(client, 'publish');
});
after(function () {
redis.createClient.restore();
});
it('sets and publishes the new value in Redis', function (done) {
myModule.updateRedis(key, oldVal, newVal);
expect(client.set.calledOnce).to.equal(true);
expect(client.publish.calledOnce).to.equal(true);
done();
});
上面的代码给出了一个断言错误(我使用Chai)
AssertionError: expected false to equal true
我也在控制台日志中收到此错误,这表示当方法实际运行时客户端没有被删除。
Error connecting to redis [Error: Ready check failed: Redis connection gone from end event.]
更新
我尝试在我的测试套件的最外层描述块中尝试删除createClient
方法(使用before
函数,以便它在我的测试之前运行)结果 - 当测试实际运行我的函数时,它似乎没有返回假客户端。
我也尝试将我的间谍放在顶级before
的{{1}}中无济于事。
我注意到当我杀死我的Redis服务器时,我收到来自Redis的连接错误消息,即使这是唯一一个触及任何使用Redis客户端的代码的测试(此刻)。我知道这是因为我在这个NodeJS服务器启动时创建客户端,Mocha将在执行测试时创建服务器应用程序的实例。我现在想的是,这不能正确获取存根的原因是因为它不仅仅是一个需求,而是在应用启动时调用describe
函数,而不是在我打电话给我正在测试的功能。 我觉得仍然应该有一种存根这种依赖关系的方法,即使它是全局的,并且在我的测试函数之前调用被存根的函数。
其他可能有用的信息:我使用的是Gulp任务运行程序 - 但我不知道这应该如何影响测试运行。
答案 0 :(得分:1)
我最终使用fakeredis
(https://github.com/hdachev/fakeredis)来删除Redis客户端,然后在我的测试套件中创建app
,如下所示:
var redis = require('fakeredis'),
konfig = require('konfig'),
redisClient = redis.createClient(konfig.redis.port, konfig.redis.host);
sinon.stub(require('redis'), 'createClient').returns(redisClient);
var app = require('../../app.js'),
//... and so on
然后我能够以正常的方式使用sinon.spy:
describe('some case I want to test' function () {
before(function () {
//...
sinon.spy(redisClient, 'set');
});
after(function () {
redisClient.set.restore();
});
it('should behave some way', function () {
expect(redisClient.set.called).to.equal(true);
});
});
也可以在客户端上模拟和存根,我发现比使用它们提供的redisErrorClient
测试回调中的Redis错误处理更好。
很明显,我不得不求助于Redis的模拟库来执行此操作,因为只要在外部作用域中调用正在测试的函数,Sinon就不能删除redisClient()方法。这是有道理的,但这是一个恼人的限制。