Node.js模块单元测试 - 用sinon.js存根异步文件系统调用

时间:2014-04-05 21:16:58

标签: javascript node.js unit-testing sinon nodeunit

我现在已经挣扎了好几个小时,而且我没有到达任何地方。 这是我正在使用的代码的简化版本:

我有一个名为' setup'内部从文件系统中读取文件,并做一些事情。我试图编写一些使用存根来返回伪造数据而不是从文件系统中读取的单元测试。我使用了nodeunit的沙箱功能,因为我不会导出我正在存根的函数。

setup
├── data.js
├── index.js
├── loader.js
├── package.json
└── test
    └── test-setup.js

index.js:

var
  loader = require( './loader' );

module.exports = function () {
  // do stuff
  loader( function ( data ) {
    console.log( "read from file: " + JSON.stringify( data, null, 2 ));
    // do more stuff
    return data;
  });
};

loader.js:

module.exports = function ( cb ) {
  var data = require( './data' );
  cb( data );
};

data.js:

module.exports = {
  data: {
    field1: "value1",
    field2: "value2"
  }
};

测试setup.js:

var
  nodeunit = require( 'nodeunit' ),
  sandbox  = require( 'nodeunit' ).utils.sandbox,
  sinon    = require( 'sinon' ),
  loader   = require( '../loader' ),
  setup    = require( '..' );

exports[ 'setup file loading' ] = nodeunit.testCase ({
  setUp: function ( callback ) {
    var
      boxModule = { exports: {} },
      boxGlobals = {
        module  : boxModule,
        exports : boxModule.exports,
        loader  : loader,
        require : function () { return loader; },
        console : console
      };
    this.fakedata = {
      fakedata: {
        field1: "value1",
        field2: "value2"
      }
    };
    this.sandbox = sandbox( '../index.js', boxGlobals );
    callback();
  },

  tearDown: function ( callback ) {
    delete this.sandbox;
    callback();
  },

  'test1: setup by loading the file normally': function ( t ) {
    t.ok( setup() === undefined, 'data is undefined - has not loaded yet' );
    t.done();
  },

  'test2: setup using sandbox and loader function replaced with stub': function ( t ) {
    var fakedata = this.fakedata;
    var returnFakeData = function () {
      return fakedata;
    };
    var stub = sinon.stub( this.sandbox, 'loader', returnFakeData);
    t.equal( this.sandbox.module.exports(), this.fakedata, 'returned value is the fakedata from the stub' );
    t.done();
  }

});

测试1通过。测试2失败: AssertionError:返回值是存根

中的伪造数据

日志消息显示它已从文件而不是fakedata打印数据。当我检查调试器中的this.sandbox.loader函数时,它被正确设置为存根。

第二个问题是我必须伪造require并传递加载器函数:

require:function(){return loader; },

否则要求找不到./loader。我尝试在测试之前使用process.chdir更改目录,但即使将cwd正确设置为项目目录,require仍然失败。显然,伪装需要只有在需要1个模块时才有效,在实际代码中有几个要求。我加载./loader的另一种方法是修改代码以使用绝对路径,每次编辑代码以运行测试都是不可行的。

我可能错过了一些明显的东西,为异步加载数据的模块代码编写单元测试必须是非常标准的东西。沙箱/短桩是正确的方法吗?

任何帮助都非常感激。

1 个答案:

答案 0 :(得分:0)

在经历了相当多的精神错乱之后,我想出了一个使用proxyquire的解决方案,其中包括代理节点需要以便在测试过程中允许覆盖依赖关系#34;。比使用nodeunit沙箱要好得多。

我还必须更新模块以接受回调函数,并修改loader.js以将函数导出为' load'。

希望这对那里的人有用。

index.js:

var
  loader = require( './loader' );

module.exports = function ( file, cb ) {
  // do stuff
  loader.load( file, function ( data ) {
    console.log( "read from file: " + JSON.stringify( data, null, 2 ));
    // do more stuff
    cb ( data );
  });
};

loader.js:

module.exports = {
  load: function ( file, cb ) {
    var data = require( file );
    cb( data );
  }
};

测试setup.js:

var
  proxyquire = require( 'proxyquire' ),
  sinon      = require( 'sinon' ),
  pathStub   = {},
  fakedata   = {
    fakedata: {
      field1: "fakevalue1",
      field2: "fakevalue2"
    }
  },
  setup = proxyquire('..', {
    './loader': pathStub
  });

exports.testSetup = function ( t ) {
  pathStub.load = sinon.stub();
  pathStub.load.yields( fakedata );

  t.expect( 1 );
  setup( './data', function ( data ) {
    t.equal( data, fakedata, 'setup returned fakedata' );
    t.done();
  });
};