1

今日のほとんどは、アプリの簡易単体テストの作成と格闘してきました。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
};

私が見逃した明白な/あいまいなものはありますか? または、私が気付いていない簡単な方法はありますか?

高速アプリでいくつかのテストを実行するだけでも、かなりの労力がかかるように思えます。

これについて何か助けていただければ幸いです。私はすべてアイデアがありません。

4

0 に答える 0