nodejs – testing失败,但callback被调用

我有一个模块,我出口,其中有一个方法editHeroImage我试图用mochachaisinon 。 模块有两个作为参数, connectionqueries传递的对象。 这些是mySql对象,其中一个包含到数据库的连接,另一个包含在其单独模块中定义的查询string。 我正在导出并试图testing的expObj是一个“帮手”模块。

我已经成功地testing了这个模块的其他方法,就像我试图testing这个方法一样,但是当我遇到由于某种原因而使用async模块的方法时,我的testing不再像预期的那样工作。 我想知道是否在这种特殊情况下丢失了某些东西,因为我testing了其他模块和方法,这些模块和方法也使用了async ,并没有遇到这种情况。

当我运行testing时,它会logging“HELLO!” 如预期的那样,但是已经调用callbackSpy的断言失败。

我在这里失去了主意! 请帮忙! 到底是怎么回事? 试衣服之间是否会有污染?

testing方法:

 expObj.editHeroImage = function(connection, queries, postId, postData, callback) { async.waterfall([ function(next) { var qString = queries.getSinglePostById(); connection.query(qString, [postId], function(err, results) { if (err) { return next(err); } if (!results.length) { console.log('NO POST FOUND WITH ID ' + postId); return callback(); } next(null, results[0].hero_image); }); }, function(heroImageId, next) { if (!heroImageId) { console.log('HERO IMAGE IS NEW - NEXT TICK!'); return next(); } // Delete resized images of hero image var queryStr = queries.deleteResizedImages(); var resizedVals = [heroImageId]; connection.query(queryStr, resizedVals, function(err) { if (err) { return callback(err); } console.log('DELETED RESIZED IMAGES OF HERO IMAGE ' + heroImageId); var qString = queries.updateHeroImagePath(); var values = [postData.hero_image, heroImageId]; return connection.query(qString, values, function(err, results) { if (err) { return next(err); } console.log('UPDATED HERO IMAGE ' + heroImageId + ' WITH PATH ' + postData.hero_image); next('break'); }); }); }, function addHeroImage(next) { var qString = queries.insertImage(); var values = [postData.hero_image, postId]; connection.query(qString, values, function(err, results) { if (err) { return next(err); } next(null, results.insertId); }); }, function addHeroImagePathToPost(heroImageId, next) { var qString = queries.saveHeroImageId(); var values = [heroImageId, postId]; connection.query(qString, values, function(err) { if (err) { return next(err); } next(); }); } ], function(err) { if (err && err !== 'break') { return callback(err); } console.log('HELLO!'); callback(null); }); }; 

testing,设置:

 'use strict'; var chai = require('chai'); var sinonChai = require("sinon-chai"); var proxyquire = require('proxyquire'); var sinon = require('sinon'); chai.use(sinonChai); var expect = chai.expect; describe('HELPERS', function() { var testedModule, callbackSpy, fakeConnectionObj, fakeQueriesObj, fakePost, fakeSnakeCaseObj, queryStub, connectionStub, manageStub, fakeCamelCaseObj; beforeEach(function() { fakePost = {}; fakeConnectionObj = {}; fakeQueriesObj = { getPostIdFromImage: function() {}, insertResizedImages: function() {}, createPost: function() {}, getPostImages: function() {}, getPostsAlternativesImages: function() {}, getSinglePostById: function() {}, getAllImages: function() {}, insertImage: function() {}, deleteMainImage: function() {}, deleteResizedImages: function() {}, updateHeroImagePath: function() {}, saveHeroImageId: function() {} }; afterEach(function() { queryStub.resetBehavior(); }); fakeSnakeCaseObj = { sub_title: '123', hero_image: '456' }; fakeCamelCaseObj = { subTitle: '123', heroImage: '456' }; callbackSpy = sinon.spy(); queryStub = sinon.stub(); manageStub = sinon.stub(); connectionStub = {query: queryStub}; testedModule = proxyquire('./../../../../lib/modules/mySql/workers/helpers', { './../../../factories/notification-service': { select: function() { return {manageSns: manageStub}; } } }); }); it('edits hero image', function() { var _post = { id: '123', title: 'vf', sub_title: 'vf', slug: 'vf', reading_time: 4, created_at: '123', published_at: '123', deleted_on: false, hero_image: 'hero_image_path' }; var _postId = '123'; queryStub.onCall(0).callsArgWith(2, null, [{hero_image: '55'}]); queryStub.onCall(1).callsArgWith(2, null); queryStub.onCall(2).callsArgWith(2, null); testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function() { console.log(arguments); // --> {'0': null} as expected callbackSpy.apply(null, arguments); }); expect(callbackSpy).has.been.calledWith(null); }); }); 

您的断言可能在asynchronous函数返回之前执行。

有很多方法可以确保你的asynchronous函数完成执行。 最干净的是格式化你的摩卡testing不同。

 describe('...', function () { var callbackSpy; before(function () { var _post = { id: '123', title: 'vf', sub_title: 'vf', slug: 'vf', reading_time: 4, created_at: '123', published_at: '123', deleted_on: false, hero_image: 'hero_image_path' }; var _postId = '123'; queryStub.onCall(0).callsArgWith(2, null, [{ hero_image: '55' }]); queryStub.onCall(1).callsArgWith(2, null); queryStub.onCall(2).callsArgWith(2, null); return testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function () { console.log(arguments); // --> {'0': null} as expected callbackSpy.apply(null, arguments); }); }); it('edits hero image', function () { expect(callbackSpy).has.been.calledWith(null); }); }); 

请注意,我已经将你的断言封装在一个描述块中,以便我们可以使用。 您设置存根和执行类的实际逻辑已被移至before块并添加了一个返回,这确保asynchronous函数在继续执行断言之前完成。

你的其他testing可能已经过去了,但是他们也会受此影响,纯粹是一个计时问题。

事实上@Varedis是正确的,这是一个时间问题。 然而,使用你的build议,包装断言在描述bloack和使用before函数来设置testing导致我的存根不再正常工作。 然而,考虑到您的时机考虑我的build议,我设法通过使用我的testing套装完成callback解决问题。 通过保持设置我做了一个小小的改变,突然间我的testing通过:

 it('edits hero image', function(done) { var _post = { id: '123', title: 'vf', sub_title: 'vf', slug: 'vf', reading_time: 4, created_at: '123', published_at: '123', deleted_on: false, hero_image: 'hero_image_path' }; var _postId = '123'; queryStub.onCall(0).callsArgWith(2, null, [{hero_image: '55'}]); queryStub.onCall(1).callsArgWith(2, null); queryStub.onCall(2).callsArgWith(2, null); testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function() { callbackSpy.apply(null, arguments); expect(callbackSpy).has.been.calledWith(null); expect(callbackSpy).has.not.been.calledWith('FDgdjghg'); done(); }); });