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



我已经为此苦苦挣扎了几个小时,但我一无所获。这是我正在使用的代码的简化版本:

我有一个名为"setup"的模块,它在内部从文件系统读取文件,并做一些事情。我正在尝试编写一些使用存根返回假数据而不是从文件系统读取的单元测试。我正在使用节点单元的沙盒功能,因为我正在存根的函数没有导出。

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

索引.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;
  });
};

装载机.js:

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

数据.js:

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

测试设置.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 失败,并显示:断言错误:返回值是来自存根的假数据

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

第二个问题是我必须伪造需求并通过以下方式传入加载器函数:

require : 函数 () { 返回加载器; },

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

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

任何帮助非常感谢。

经过相当多的疯狂之后,我想出了一个使用proxyquire的解决方案,"代理nodejs需要允许在测试期间覆盖依赖项"。比使用节点单元沙箱要好得多。

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

希望这对那里的人有用。

索引.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 );
  });
};

装载机.js:

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

测试设置.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();
  });
};

最新更新