【问题标题】:How to stub a method that is called from an outer scope to the function under test?如何存根从外部范围调用的方法到被测函数?
【发布时间】:2016-01-19 08:59:22
【问题描述】:

我有一个使用 node_redis 库 (https://github.com/NodeRedis/node_redis) 创建的 Redis 客户端:

var client = require('redis').createClient(6379, 'localhost');

我有一个我想测试的方法,其目的是设置和发布一个值到 Redis,所以我想测试以确保根据我的期望调用或不调用 setpublish 方法。棘手的是我希望这个测试能够在不需要启动 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 并没有被标记为对我来说是真的。如果我 stub 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 函数,以便它在我的测试之前运行),结果相同 - 它似乎没有'当测试实际运行我的函数时,不返回假客户端。

我也试过将我的间谍放在顶级describebefore 中,但无济于事。

我注意到,当我终止我的 Redis 服务器时,我从 Redis 收到连接错误消息,尽管这是(目前)唯一涉及使用 Redis 客户端的任何代码的测试。我知道这是因为我在此 NodeJS 服务器启动时创建了客户端,而 Mocha 将在执行测试时创建服务器应用程序的实例。我现在假设这没有得到正确存根的原因不仅仅是因为它不仅仅是一个要求,而是在应用程序启动时调用 createClient() 函数,而不是在我调用正在测试的函数时。 我觉得仍然应该有一种方法来存根这种依赖关系,即使它是全局的并且被存根的函数在我的测试函数之前被调用

其他可能有用的信息:我正在使用 Gulp 任务运行程序 - 但我看不出这会如何影响测试的运行方式。

【问题讨论】:

    标签: javascript node.js mocha.js sinon stubbing


    【解决方案1】:

    在我的测试套件中创建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 就无法存根。这是有道理的,但这是一个令人讨厌的限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-21
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 2021-01-15
      • 1970-01-01
      相关资源
      最近更新 更多