今日のほとんどは、アプリの簡易単体テストの作成と格闘してきました。app.js でアプリのセットアップを行う非同期関数があります。大量のファイルを読み取るため、時間がかかります。アプリの実行は問題ではありませんが、単体テストは、テストの実行時にセットアップがまだ完了していないためです。
そこで、非同期関数をスタブ化し、コールバックが実行されるまで単体テスト setUp を完了しない再定義された非同期関数を呼び出すようにしました。
問題は、再定義された関数が実行されるときに、テストによってアプリがセットアップに渡されたとしても、渡されたアプリ インスタンスですべてのセットアップが完了していないことです。sinon.callsArgWith が引数の周りにクロージャーを設定しているように見えるため、関数が実行されると、設定前の状態の引数が含まれます。
また、proxyquire を使用して、テスト実行の間にモジュールの新しいロードを確実に取得しています (noPreserveCache を使用)。
再定義された関数は options 引数で渡され、sinon はそれを探して実行します。これが本来のやり方だと思います。ドキュメントを 20 回読んだのですが、コードを実行する他の方法を見つけることができませんでした。 (シノンの正しい使い方ですか?)
Express アプリ (v4) の構造は次のとおりです。
.
├── app.js
├── bin
│ └── www
├── lib
│ ├── async_function.js
│ ├── error_handlers.js
│ ├── helpers.js
│ └── options.js
├── package.json
└── test
└── test.js
www:
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 );
});
app.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 );
};
options.js:
module.exports = {
arg1: "qwertyuiop",
arg2: "asdfghjkl"
};
test.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
};
私が見逃した明白な/あいまいなものはありますか? または、私が気付いていない簡単な方法はありますか?
高速アプリでいくつかのテストを実行するだけでも、かなりの労力がかかるように思えます。
これについて何か助けていただければ幸いです。私はすべてアイデアがありません。