如何将从外部范围调用的方法存根到被测函数?
How to stub a method that is called from an outer scope to the function under test?
我有一个使用 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,但都没有成功。如果我尝试监视这些方法,我可以告诉 运行 调试器正在调用我的方法,但 calledOnce 没有被标记为对我来说是真的。如果我将 createClient
方法存根到 return 假客户端,例如:
{
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
我还在控制台日志中收到此错误,这表明客户端在方法实际 运行s 时未被删除。
Error connecting to redis [Error: Ready check failed: Redis connection gone from end event.]
更新
我已经尝试在我的最外层描述块中删除 createClient
方法(使用 before
函数以便它在我的测试之前 运行s)具有相同结果的测试套件 - 当测试实际上 运行 是我的功能时,它似乎不是 return 假客户端。
我也试过把我的间谍放在顶层describe
的before
里,但没有用。
我注意到,当我关闭我的 Redis 服务器时,我会收到来自 Redis 的连接错误消息,尽管这是(目前)唯一涉及使用 Redis 客户端的任何代码的测试。我知道这是因为我在这个 NodeJS 服务器启动时创建了客户端,而 Mocha 将在执行测试时创建服务器应用程序的实例。我现在假设这没有被正确存根的原因是因为它不仅仅是一个要求,而是在应用程序启动时调用 createClient()
函数,而不是在我调用正在测试的函数时. 我觉得仍然应该有一种方法来存根这种依赖性,即使它是全局的并且被存根的函数在我的测试函数之前被调用。
其他可能有用的信息:我正在使用 Gulp 任务 运行ner - 但我不明白这应该如何影响测试 运行.
在我的测试套件中创建 app
之前,我最终使用 fakeredis
(https://github.com/hdachev/fakeredis) 将 Redis 客户端存根,如下所示:
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 的模拟库来执行此操作,因为只要在被测函数的外部范围内调用 redisClient() 方法,Sinon 就无法将其存根.这是有道理的,但这是一个烦人的限制。
我有一个使用 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,但都没有成功。如果我尝试监视这些方法,我可以告诉 运行 调试器正在调用我的方法,但 calledOnce 没有被标记为对我来说是真的。如果我将 createClient
方法存根到 return 假客户端,例如:
{
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
我还在控制台日志中收到此错误,这表明客户端在方法实际 运行s 时未被删除。
Error connecting to redis [Error: Ready check failed: Redis connection gone from end event.]
更新
我已经尝试在我的最外层描述块中删除 createClient
方法(使用 before
函数以便它在我的测试之前 运行s)具有相同结果的测试套件 - 当测试实际上 运行 是我的功能时,它似乎不是 return 假客户端。
我也试过把我的间谍放在顶层describe
的before
里,但没有用。
我注意到,当我关闭我的 Redis 服务器时,我会收到来自 Redis 的连接错误消息,尽管这是(目前)唯一涉及使用 Redis 客户端的任何代码的测试。我知道这是因为我在这个 NodeJS 服务器启动时创建了客户端,而 Mocha 将在执行测试时创建服务器应用程序的实例。我现在假设这没有被正确存根的原因是因为它不仅仅是一个要求,而是在应用程序启动时调用 createClient()
函数,而不是在我调用正在测试的函数时. 我觉得仍然应该有一种方法来存根这种依赖性,即使它是全局的并且被存根的函数在我的测试函数之前被调用。
其他可能有用的信息:我正在使用 Gulp 任务 运行ner - 但我不明白这应该如何影响测试 运行.
在我的测试套件中创建 app
之前,我最终使用 fakeredis
(https://github.com/hdachev/fakeredis) 将 Redis 客户端存根,如下所示:
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 的模拟库来执行此操作,因为只要在被测函数的外部范围内调用 redisClient() 方法,Sinon 就无法将其存根.这是有道理的,但这是一个烦人的限制。