简体   繁体   English

如何将从外部作用域调用的方法存根到被测函数?

[英]How to stub a method that is called from an outer scope to the function under test?

I have a Redis client that is created thus using the node_redis library ( https://github.com/NodeRedis/node_redis ): 我有一个使用node_redis库创建的Redis客户端( https://github.com/NodeRedis/node_redis ):

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

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. 我有一个我想测试的方法,其目的是设置和发布Redis的值,所以我想测试以确保根据我的期望调用或不调用setpublish方法。 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. 棘手的是我希望这个测试能够在不需要启动Redis服务器实例的情况下工作,因此我不能只创建客户端,因为如果无法检测到Redis,它将抛出错误。 Therefore, I need to stub the createClient() method. 因此,我需要存根createClient()方法。

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

I've tried several ways of testing whether set and publish are called with the expected key and value, but have been unsuccessful. 我已经尝试了几种方法来测试是否使用预期的键和值调用set和publish,但是没有成功。 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. 如果我试图窥探这些方法,我可以告诉我的方法是通过运行调试器来调用的,但是对于我来说,callOnce并没有被标记为真。 If I stub the createClient method to return a fake client, such as: 如果我将createClient方法存根以返回假客户端,例如:

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

The method under test doesn't appear to be using the fake client. 测试中的方法似乎没有使用假客户端。

Right now, my test looks like this: 现在,我的测试看起来像这样:

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

The above code gives me an Assertion error (I'm using Chai) 上面的代码给了我一个断言错误(我正在使用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.]

UPDATE UPDATE

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. 我已经尝试在我的测试套件的最外层描述块中尝试删除createClient方法(使用before函数以便它在我的测试之前运行)具有相同的结果 - 它似乎不会返回假客户端时测试实际上运行我的功能。

I've also tried putting my spies in the before of the top-level describe to no avail. 我也试过把我的间谍放在顶级describe before无济于事。

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. 我注意到当我杀死我的Redis服务器时,我从Redis收到连接错误消息,即使这是唯一一个触及任何使用Redis客户端的代码的测试(此刻)。 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. 我知道这是因为我在这个NodeJS服务器启动时创建客户端,Mocha将在执行测试时创建服务器应用程序的实例。 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. 我现在想的是,这不是正确的原因是因为它不仅仅是一个需求,而是在app启动时调用createClient()函数,而不是在我调用我正在测试的函数时。 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 . 我觉得仍然应该有一种方法来存根这种依赖,即使它是全局的,并且在我的测试函数之前调用被存根的函数

Other potentially helpful information: I'm using the Gulp task runner - but I don't see how this should affect how the tests run. 其他可能有用的信息:我正在使用Gulp任务运行器 - 但我不知道这应该如何影响测试的运行方式。

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: 在我的测试套件中创建app之前,我最终使用fakeredishttps://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

And then I was able to use sinon.spy in the normal way: 然后我能够以正常的方式使用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);
  });
});

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. 也可以在客户端上模拟和存根,我发现它比使用它们提供的redisErrorClient更好地测试回调中的Redis错误处理。

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. 显而易见的是,我不得不求助于Redis的模拟库来执行此操作,因为只要在外部作用域中调用正在测试的函数,Sinon就不能存根redisClient()方法。 It makes sense, but it's an annoying restriction. 这是有道理的,但这是一个恼人的限制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM