使用摩卡混合同步和asynchronoustesting

我有一个函数,计算一些东西,通过callback关于一些事件通知用户:

function returnAndCallback(callback) { callback(5); // not always invoked return 3; } 

使用Mocha和Should.js,我写了这个testing:

 describe('mixing sync and async code', function() { it('should test callback AND return value', function(done) { returnAndCallback(function(value) { value.should.be.eql(5); done(); }).should.be.eql(4); }); }); 

这是成功的,因为testingdone()done()被调用。 看来,我可以写一个同步testing,并检查返回值,或者我可以写一个asynchronoustesting,并检查callback。

不能使用像这样的同步testing:

 describe('mixing sync and async code', function() { it('should test callback AND return value', function() { returnAndCallback(function(value) { should.be.eql('difficult to get result'); }).should.be.eql(3); }); }); 

…因为我想断言callback被调用。 如果callback从未被调用,这个testing会成功。

我怎样才能testingcallback被调用与正确的价值正确的价值被返回?

重复testing是我看到的唯一select。

编辑:我注意到,我在这里错误地使用术语asynchronous。 callback只是将可选操作注入到一个函数中的一种方法,该函数转换了一个对象。 所有的代码是同步的,但控制stream有时分支到callback,我想能够认识到这一点。

这是另一种可能的方法来testing:

 describe('mixing sync and async code', function() { it('should test callback AND return value', function() { let callbackValue; returnAndCallback(function(value) { callbackValue = value; }).should.be.eql(3); callbackValue.should.be.eql(5); }); }); 

但由于额外的variables,仍然不完美。

首先,请注意done()意味着同步testing; 摩卡的默认是asynchronous运行testing。 如果要testingasynchronous函数(在callback函数中返回值的函数)中的“返回”值,可以通过done()同步运行它们。

接下来,您不能从asynchronous函数返回值。 这两种行为是相互排斥的:

 function returnAndCallback(callback) { callback(5); // not always invoked return 3; } 

你只想执行callback。

在我看来,你期望有时callback通过,但并不总是。 在这种情况下,我会分开functiontesting(我认为你需要在任何地方都使用done()来坚持testing的同步性),并在函数本身内部检查callback。

现在我们已经弄清楚了,因为您要声明一个callback被调用,所以我们需要build立一些基本的假设:

  • 答)一个callback应该是一个函数
  • B)应使用包含值的参数调用callback

你想testing这两件事情。 答)很容易certificate:你正在写callback函数作为你的testing的一部分,所以如果你通过say, null或者undefined ,当然testing会失败,但这不是testing的要点。 这是你如何certificateA)和B):

 function optionalAsync(callback) { if (typeof callback === 'function') { callback(4) } else { return 3 } } describe('optional async function', function() { it('should return 4 when a callback is passed', function(done) { optionalAsync(function(value) { should(value).be.eql(4) done() }) }) it('should return 3 when no callback is passed', function(done) { optionalAsync().should.be.eql(3) done() }) }) 

这有点奇怪,但考虑到你的用例,检查两种可能性是有意义的。 我相信你可以减less代码的占用量,但是为了可读性,我build议保持这种方式,当你搁置一年,忘记你做了什么;)

现在所有这一切,如果你仍然想要一个function同步运行的选项,你可以通过阻止事件循环: https : //stackoverflow.com/a/22334347/1214800 。

但是,你为什么要?

在自己的非阻塞的asynchronous平台中处理同步操作的麻烦,并用callback函数写入所有内容(即使是非IO阻塞操作):

 function optionallyLongRunningOp(obj, callback) { if (typeof callback === 'function') { validateObject(obj, function(err, result) { // not always long-running; may invoke the long-running branch of your control-flow callback(err, result) }) } else { throw new Error('You didn't pass a callback function!') } } describe('possibly long-running op async function', function() { it('should return 4 when control flow A is entered', function(done) { obj.useControlFlow = "A" validateObject(obj, function(err, result) { // this is a slow return should(result.value).be.eql(4) done() }) it('should return 3 when control flow B is entered', function(done) { obj.useControlFlow = "B" validateObject(obj, function(err, result) { // this is a quick return should(result.value).be.eql(3) done() }) }) }) 

这里是你回答的所有答案(即使是短的操作):

 var doLongRunnignOp = function(cb) { var didNotify = true cb(didNotify) } function doubleAndNotifyEven(num, cb) { if (num % 2 == 0) { doLongRunnignOp(function(didNotify) { cb(num) // did notify }) } else { cb(2 * num) // instant return, did not notify } } describe('checking return value and callback execution', function() { it('should double and report given an even number', function() { doubleAndNotifyEven(2, function(value) { should(value).be.eql(2) }) }) it('should double and not report anything given an odd number', function() { doubleAndNotifyEven(3, function(value) { should(value).be.eql(6) }) }) }) 

这是另一个解决这个问题的方法。 它只是在想要确保callback执行的testing中增加一行代码。

 let should = require('should'); function doubleAndNotifyEven(num, reportEven) { if (num % 2 == 0) { reportEven(num); } return 2 * num; } function mandatoryCallback(callback) { mandatoryCallback.numCalled = 0; return function () { mandatoryCallback.numCalled++; return callback.apply(this, arguments); }; } describe('checking return value and callback execution', function() { it('should double and report given an even number', function() { doubleAndNotifyEven(2, mandatoryCallback(function(value) { should(value).be.eql(2); })).should.be.eql(4); should(mandatoryCallback.numCalled).greaterThan(0); }); it('should double and not report anything given an odd number', function() { doubleAndNotifyEven(3, function(value) { throw new Error('Wrong report!'); }).should.be.eql(6); }); }); 

还请注意做一些类似的sinon 。