0

我正在处理混合节点样式回调和 Bluebird 承诺的代码,我需要为它编写一些单元测试。

特别是,cache.js公开了init()与 Promise 一起使用的函数。然后它被doSomething()另一个文件(例如index.js)中的函数调用,该文件又接受必须在init().

伪代码如下:

// [ cache.js ]
function init() {
  return performInitialisation()
    .then((result) => return result);
}


// [ index.js ]
var cache = require('./cache');

function doSomething(callback) {
  console.log('Enter');

  cache.init()
    .then(() => {
      console.log('Invoking callback');
      callback(null);
    })
    .catch((err) => {
      console.log('Invoking callback with error');
      callback(err);
    });

  console.log('Exit');
}

可能的单元测试可能是(仅显示相关代码):

// [ index.test.js ]
...
var mockCache = sinon.mock(cache);
...
it('calls the callback on success', function(done) {
  mockCache.expects('init')
    .resolves({});

  var callback = sinon.spy();

  doSomething(callback);
  expect(callback).to.have.been.calledOnce;
  done();
});

该测试通过,但是将期望更改为not.have.been.calledOnce也通过,这是错误的。

此外,控制台日志乱序:

Enter
Exit
Invoking callback

我研究了几种可能性,但都没有奏效:

  • 使用 chai-as-promised,例如expect(callback).to.eventually.have.been.calledOnce;

  • 重构doSomething()简单:

    函数 doSomething(callback) { cache.init() .asCallback(callback); }

谁能帮我理解我做错了什么以及如何解决它?

4

2 回答 2

0

詹姆斯的评论之后,我重新审视了我的测试,如下所示:

it('calls the callback on success', function(done) {
  mockCache.expects('init')
    .resolves({});

  doSomething(done);
});

it('calls the callback on error', function(done) {
  mockCache.expects('init')
    .rejects('Error');

  doSomething((err) => {
    if (err === 'Error') {
      done();
    } else {
      done(err);
    }
  });
});
于 2017-08-03T15:00:17.987 回答
0

控制台日志乱序

日志的顺序正确,因为您Promise将是异步的,这意味着至少内部控制台记录调用then&catch将在下一个滴答声中运行。

至于为什么测试失败是几个问题的结果,第一个是你似乎没有sinon-chai正确配置,或者充其量你的calledOnce断言没有生效。只是为了确认,你的测试文件的顶部应该是这样的:

const chai = require("chai");
const sinonChai = require("sinon-chai");

chai.use(sinonChai);

如果你有它并且它仍然无法正常工作,那么可能值得在sinon-chailib 上打开一个问题,但是,一个简单的解决方法是切换到sinon 断言,例如

sinon.assert.calledOnce(callback)

其次,当您最终解决此问题时,您可能会发现测试现在将失败……每次。原因是您在测试中遇到了与日志记录相同的问题 - 您在内部承诺有机会解决之前断言。解决此问题的最简单方法实际上是使用doneMocha 的处理程序作为断言

mockCache.expects('init').resolves({});
doSomething(() => done());

换句话说,如果doneget 被调用,那么你就知道回调已经被调用了 :)

于 2017-08-02T12:40:44.387 回答