【问题标题】:nodejs - test failing but callback being callednodejs - 测试失败但回调被调用
【发布时间】:2017-03-07 10:37:31
【问题描述】:

我有一个要导出的模块,它有一个方法editHeroImage,我正在尝试使用mocha、chai 和sinon 对其进行测试。这些模块有两个作为参数传递的对象,connection 和 queries。这些是mySql 对象,一个包含与数据库的连接,另一个包含在其单独模块中定义的查询字符串。我正在导出并尝试测试的 expObj 是一个“帮助”模块。

我已经成功地测试了这个模块的其他方法,就像我尝试测试这个方法一样,但是,当我遇到由于某种原因使用async 模块的方法时,我的测试不再像预期的那样运行.我想知道在这种特殊情况下我是否遗漏了什么,因为我已经测试了其他也使用 async 的模块和方法并且没有遇到这种行为。

当我运行测试时,它会记录“HELLO!”正如预期的那样,但 callbackSpy 已被调用的断言失败。

我在这里疯了!请帮忙!到底是怎么回事?测试服之间会不会有污染?

待测方法:

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

测试,设置:

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

【问题讨论】:

    标签: node.js unit-testing mocha.js sinon chai


    【解决方案1】:

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

    有多种方法可以确保您的异步函数已完成执行。最干净的方法是以不同的方式格式化你的 mocha 测试。

    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。您设置存根和执行类的实际逻辑已移至 before 块并添加了返回,这可确保异步函数在继续您的断言之前完成。

    您的其他测试可能已通过,但它们也会受到此影响,这纯粹是时间问题。

    【讨论】:

    • 确实,您认为这是一个时间问题是对的。但是,使用您将断言包装在描述块中并使用 before 函数设置测试的建议导致我的存根不再正常工作。但是,考虑到您对时间的建议,我设法通过在我的测试服中使用 done 回调解决了这个问题。
    【解决方案2】:

    确实,@Varedis 认为这是一个时间问题是正确的。但是,使用您将断言包装在描述块中并使用 before 函数设置测试的建议导致我的存根不再正常工作。但是,考虑到您对时间的建议,我设法通过在我的测试服中使用 done 回调解决了这个问题。通过保持设置,我做了一点改动,我的测试突然通过了:

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

    【讨论】:

      猜你喜欢
      • 2016-02-23
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 2012-01-23
      • 1970-01-01
      • 2023-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多