我今天大部分时间都在努力为我的应用程序编写一些快速的单元测试。我有一个异步函数,它在 app.js 中进行一些应用程序设置,它读取大量文件,因此需要一段时间。运行应用程序不是问题,但单元测试是因为当测试运行时,设置尚未完成。
所以我想我会存根异步函数,让它调用一个重新定义的异步函数,该函数在运行回调之前不会完成单元测试设置。
问题是,当重新定义的函数运行时,它通过的应用程序实例尚未完成所有设置,即使测试将应用程序传递给设置。似乎 sinon.callsArgWith 围绕参数设置了一个闭包,以便在函数运行时,它具有具有预设置状态的参数。
我还使用 proxyquire 来确保在测试运行之间获得新的模块负载(使用 noPreserveCache)。
重新定义的函数在options参数中传递,sinon查找并运行它,我认为这是应该的,我阅读文档20次,找不到任何其他方法来运行代码(这是正确使用 sinon 的方法吗?)
express 应用程序 (v4) 具有以下结构:
.
├── app.js
├── bin
│ └── www
├── lib
│ ├── async_function.js
│ ├── error_handlers.js
│ ├── helpers.js
│ └── options.js
├── package.json
└── test
└── test.js
万维网:
var
app = require( '../app' ),
server;
app.set( 'port', process.env.PORT || 3000 );
server = app.listen( app.get( 'port' ), function () {
console.log( "Express server listening on port " + server.address().port );
});
应用程序.js:
var
async_function = require( './lib/async_function' ),
express = require( 'express' ),
path = require( 'path' ),
app = express(),
options = require( './lib/options' ),
setupErrorHandlers = require( './lib/error_handlers' );
;
// do some app setup stuff
app.set( 'views', path.join( __dirname, 'views' ) );
app.set( 'view engine', 'hogan' );
app.disable( "x-powered-by" );
async_function( app, options, function ( err ) {
if ( err ) console.log( "Error: " + err );
setupErrorHandlers( app );
});
module.exports = app;
async_function.js:
module.exports = function ( app, options, cb ) {
setTimeout( function () {
if ( app.result ) {
app.result += options.arg1 + options.arg2;
}
else {
app.result = options.arg1 + options.arg2;
}
console.log( "async_function.js: app.result: " + app.result );
cb();
}, 10000 );
};
选项.js:
module.exports = {
arg1: "qwertyuiop",
arg2: "asdfghjkl"
};
测试.js:
var
nodeunit = require( 'nodeunit' ),
express = require( 'express' ),
helpers = require( '../lib/helpers' );
exports[ 'stubbing a non returning async function with a custom function' ] = nodeunit.testCase({
setUp: function ( callback ) {
var async_path = '../lib/async_function';
this.app = express();
var options = {
arg1: "zxcvbnm",
arg2: "1234567890"
};
helpers.setupTestExpressApp( async_path, this.app, options, callback );
},
tearDown: function ( callback ) {
delete this.app;
callback();
},
'test1': function ( t ) {
t.expect( 2 );
t.ok( ( this.app.result !== undefined ), 'async_function produced a result' );
t.equal( this.app.result, 'zxcvbnm1234567890', 'custom async function was called' );
t.done();
},
'test2': function ( t ) {
t.expect( 2 );
t.ok( ( this.app.result !== undefined ), 'async_function produced a result' );
t.equal( this.app.result, 'zxcvbnm1234567890', 'custom async function was called' );
t.done();
}
});
helpers.js:
var
proxyquire = require( 'proxyquire' ).noPreserveCache(),
sinon = require( 'sinon' );
var setupTestExpressApp = function ( async_path, app, options, callback ) {
var
options, app, cb,
async_function, async_function_stub, redefined_async_function;
// Get a fresh copy of the async_function
async_function = proxyquire( async_path, { } );
// Create a stub
async_function_stub = sinon.stub();
cb = function ( err ) {
if ( err ) console.log( "Error: " + err );
console.log( "Setup complete redefined" );
callback();
};
// Create the re-defined function
redefined_async_function = function ( app, options, cb ) {
async_function( app, options, cb );
};
// Instruct the stub to use the custom function with the prepared args
async_function_stub.callsArgWith( 1, app, options, cb );
// Run the app setup, passing in the stub, the express app and sneaking in the
// redefined function in the options arg
app = proxyquire( '../app', {
'./lib/async_function': async_function_stub,
app: app,
'./lib/options': redefined_async_function
});
};
module.exports = {
setupTestExpressApp : setupTestExpressApp
};
有什么我错过的明显/模糊的东西吗?或者一些我不知道的更简单的方法?
我想会有,只是在一个快速应用程序上运行一些测试似乎需要付出很多努力。
我真的很感激一些帮助,我完全没有想法。