如何将从外部范围调用的方法存根到要测试的函数? [英] How to stub a method that is called from an outer scope to the function under test?

查看:134
本文介绍了如何将从外部范围调用的方法存根到要测试的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Redis client that is created thus using the node_redis library (https://github.com/NodeRedis/node_redis):

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

我有一个要测试的方法,该方法的目的是设置值并将其发布到Redis,因此我想测试以确保根据我的期望调用或不调用setpublish方法.棘手的事情是我希望该测试能够正常运行而无需启动Redis服务器实例,因此我不能仅仅创建客户端,因为如果它无法检测到Redis,它将抛出错误.因此,我需要对createClient()方法进行存根.

I have a method I want to test whose purpose is to set and publish a value to Redis, so I want to test to ensure the set and publish methods are called or not called according to my expectations. The tricky thing is I want this test to work without needing to fire up an instance of a Redis server, so I can't just create the client because it will throw errors if it cannot detect Redis. Therefore, I need to stub the createClient() method.

示例方法:

// 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,但是均未成功.如果我尝试监视方法,则可以通过运行调试器来告诉我的方法正在被调用,但是对我来说,namedOnce不会被标记为true.如果我对createClient方法存根以返回假客户端,例如:

I've tried several ways of testing whether set and publish are called with the expected key and value, but have been unsuccessful. If I try to spy on the methods, I can tell my methods are getting called by running the debugger, but calledOnce is not getting flagged as true for me. If I stub the createClient method to return a fake client, such as:

{
  set: function () { return 'OK'; },
  publish: function () { return 1; }
}

被测方法似乎没有使用假客户端.

The method under test doesn't appear to be using the fake client.

现在,我的测试如下:

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)

The above code gives me an Assertion error (I'm using Chai)

AssertionError: expected false to equal true

在控制台日志中我也收到此错误,这表明在该方法实际运行时客户端没有被拖出.

I also get this error in the console logs, which indicates the client isn't getting stubbed out when the method actually runs.

Error connecting to redis [Error: Ready check failed: Redis connection gone from end event.]

更新

此后,我尝试在测试套件的最外面的describe块中存根createClient方法(使用before函数,以便在测试之前运行),结果相同-看来它没有测试实际运行我的功能时,不会返回假客户端.

I've since tried stubbing out the createClient method (using the before function so that it runs before my tests) in the outer-most describe block of my test suite with the same result - it appears it doesn't return the fake client when the test actually runs my function.

我还试图将间谍放在顶级describebefore中,但无济于事.

I've also tried putting my spies in the before of the top-level describe to no avail.

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

I noticed that when I kill my Redis server, I get connection error messages from Redis, even though this is the only test (at the moment) that touches any code that uses the Redis client. I am aware that this is because I create the client when this NodeJS server starts and Mocha will create an instance of the server app when it executes the tests. I'm supposing right now that the reason this isn't getting stubbed properly is because it's more than just a require, but the createClient() function is being called at app startup, not when I call my function which is under test. I feel there still ought to be a way to stub this dependency, even though it's global and the function being stubbed gets called before my test function.

其他可能有用的信息:我正在使用Gulp任务运行器-但我看不到这将如何影响测试的运行.

Other potentially helpful information: I'm using the Gulp task runner - but I don't see how this should affect how the tests run.

推荐答案

我最终使用了fakeredis( https: //github.com/hdachev/fakeredis )在我的测试套件中创建app之前先存入Redis客户端,如下所示:

I ended up using fakeredis(https://github.com/hdachev/fakeredis) to stub out the Redis client BEFORE creating the app in my test suite like so:

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:

And then I was able to use sinon.spy in the normal way:

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);
  });
});

还可以在客户端上模拟和存根代码,这比使用它们提供的用于测试回调中Redis错误处理的redisErrorClient更好.

It's also possible to mock and stub things on the client, which I found better than using the redisErrorClient they provide for testing Redis error handling in the callbacks.

很明显,我必须使用Redis的模拟库来做到这一点,因为Sinon不能存根redisClient()方法,只要它在被测函数的外部作用域中被调用即可.这是有道理的,但这是一个令人讨厌的限制.

It's quite apparent that I had to resort to a mocking library for Redis to do this because Sinon couldn't stub out the redisClient() method as long as it was being called in an outer scope to the function under test. It makes sense, but it's an annoying restriction.

这篇关于如何将从外部范围调用的方法存根到要测试的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆