Problem summary: 我想测试 redisCallback() ,它是在NodeJS异步回调中调用的(参见下面的代码),已使用预期的参数调用预期的次数 .

background information (skip if TL;DR):

我四处搜索但只找到了QUnit的解决方案 . 我不确定Mocha Sinon相当于这种困境的等价物 .

在我目前的测试中,Chai断言以 AssertionError: expected false to equal true 响应 .

但是,我通过在 updateRedis 函数(正在测试中)中调用 this.redisCallback 来测试它,但在 redisClient.get 异步回调之外,在这种情况下,测试套件正确检测到回调已被触发并且我的测试成功 .

这告诉我的是这是一个异步问题 . 在 redisClient.get 回调完成之后,我如何告诉Mocha / Chai等待检查断言?

Code

编写此代码是为了解决Node的问题,我需要检查Redis中的密钥是否与系统命令返回的值不同,如果更改了,请在Redis中设置新值 . 由于系统命令值将在异步调用的包含范围内(因此,不可访问/不可靠),我创建了一个函数,它返回我想要的redisClient.get回调的回调,但是将使用的密钥和系统命令值传递给用于记忆回调值的闭包 .

接近我使用HighlandJS编写的内容:

var redisClient = require('redis').createClient(config.redis.port, config.redis.host),
    _ = require('highland');

// ...

redisCallback = function (resultsHash, redisKey, redisGetValue) {
  var systemCmdValue = resultsHash[redisKey];
  if (redisGetValue != systemCmdValue) {
      redisClient.set(redisKey, systemCmdValue, function (err) { // handle errors });
  }
};

// ...

updateRedis = function (err, stdout, stderr) {
  // Do some stuff that's not important to this problem

  var resultsHash = parseCmdResults(stdout),
      redisKeys = Object.keys(resultsHash),
      curriedRedisCallback = _.curry(this.redisCallback, resultsHash);
      get = _.wrapCallback(redisClient.get.bind(redisClient));

  redisKeys.each(function (key) {
    get(key).each(curriedRedisCallback(key));
  });
};

vanilla NodeJS和JavaScript中的等效实现:

var redisClient = require('redis').createClient(config.redis.port, config.redis.host);

// ...

redisCallback = function (resultsHash, redisKey, redisGetValue) {
  var systemCmdValue = resultsHash[redisKey];
  if (redisGetValue != systemCmdValue) {
    redisClient.set(redisKey, systemCmdValue, function (err) { // handle errors });
  }
};

redisCallbackWrapper = function (resultsHash) {
  return function (redisKey) {
    return function (redisGetValue) {
      this.redisCallback(resultsHash, redisKey, redisGetValue);
    };
  };
};

// ...

updateRedis = function (err, stdout, stderr) {
  // Do some stuff that's not important to this problem

  var resultsHash = parseCmdResults(stdout),
      redisKeys = Object.keys(resultsHash),
      callback = redisCallbackWrapper(resultsHash),
      key;

  for (var i = 0; i < redisKeys; i++) {
    key = redisKeys[i];
    redisClient.get(key, callback(key));
  }
};

我使用Mocha,Chai和Sinon的测试:

describe('when valid arguments are provided to updateRedis', function () {
  var err, stdout, stderr, parsedStdout, key0, key1;

  before(function () {
    err = undefined;
    stderr = undefined;
    stdout = 'key0/value0\nkey1/value1';
    key0 = 'key0';
    key1 = 'key1';
    parsedStdout = {};
    parsedStdout[key0] = "value0";
    parsedStdout[key1] = "value1";

    sinon.spy(myModule, 'redisCallback');
  });

  after(function () {
    myModule.redisCallback.restore();
  });

  it('calls redisCallback with the expected arguments the expected number of times', function (done) {
    myModule.updateRedis(err, stdout, stderr);
    // If I can get to the point where it's detected that redisCallback has been called at all,
    // I can follow the documentation to figure out how to test that it's called with the expected args the expected number of times.
    expect(myModule.redisCallback.called).to.equal(true);
    done();
  });
});