web-dev-qa-db-ja.com

nodejsでミドルウェア/コントローラーをテストするためにリクエストとレスポンスをモックする方法は?

私のアプリケーションには、ミドルウェア、コントローラー、マネージャーのいくつかのレイヤーがあります。 Controllersインターフェースは、ミドルウェア1と同じです:(req、res、next)。

したがって、私の質問は次のとおりです。サーバーを起動せず、ローカルホストに「実際の」リクエストを送信せずにコントローラーをテストするにはどうすればよいですか。私がしたいのは、nodejsのようにリクエスト、レスポンスのインスタンスを作成し、コントローラのメソッドを呼び出すことです。

このようなもの:

var req = new Request()
var res = new Response()
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)

どんなアドバイスも大歓迎です。ありがとう!

追伸これを行う理由は、最後に、応答オブジェクトにヒスイビューの正しい変数が含まれているかどうかをテストするためです。

32
potomok

node-mocks-http に、ある程度まともな実装があります

それが必要です:

var mocks = require('node-mocks-http');

その後、reqおよびresponseオブジェクトを作成できます。

req = mocks.createRequest();
res = mocks.createResponse();

その後、コントローラーを直接テストできます。

var demoController = require('demoController');
demoController.login(req, res);

assert.equal(res.json, {})

注意

この実装には、イベントエミッターが起動されないことに関係する問題を書いている時点であります。

18
superluminary

JavaScriptは動的に型指定された言語であるため、次のようにモックオブジェクトを作成し、コントローラーに渡すことができます。

var req = {};
var res = {};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)

コントローラーが要求または応答オブジェクトからの特定のデータまたは機能を必要とする場合、モックでそのようなデータまたは機能を提供する必要があります。例えば、

var req = {};
req.url = "http://google.com"; // fake the Url

var res = {};
res.write = function(chunk, encoding) { 
  // fake the write method 
};

var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
9
Hector Correa

このために dupertest を使用してみます。これは、新しいサーバーを起動することなく、簡単にコントローラーをテストするために作成したノードモジュールです。

requestsupertestなどのノードモジュールの使い慣れた構文を保持しますが、サーバーをスピンアップする必要はありません。

上記で提案したHectorによく似ていますが、Jasmineなどのテストフレームワークと統合して、もう少しシームレスに感じます。

質問に関する例は次のようになります。

request(controller.get_user)
  .params({id: user_id})
  .expect(user, done);

または、より明示的なロングハンドバージョン:

request(controller.get_user)
  .params({id: user_id})
  .end(function(response) {
    expect(response).toEqual(user);
    done();
  });

注:例ではuser_iduserはどこかで定義されており、コントローラはidに基づいてユーザーを取得して返します。

編集:上記の回答に対するあなたの応答を読んで、私は現在、このモジュールはデフォルトでより堅牢なモック要求または応答オブジェクトを統合していないことを認めます。 dupertestを使用すると、プロパティをreqresの両方に簡単に拡張および追加できますが、デフォルトではそれらはほとんど必要ありません。

6
tydotg

少し古い投稿ですが、2セントを差し上げたいと思います。採用するアプローチは、単体テストを行うか統合テストを行うかによって異なります。スーパーテストの使用ルートを進んでいる場合、実際の実装コードを実行していること、つまり統合テストを実行していることを意味します。それがあなたがしたいことであれば、このアプローチは問題ありません。

ただし、単体テストを実行している場合、reqおよびresオブジェクト(および関連するその他の依存関係)をモックすることになります。以下のコード(簡潔にするために関連性のないコードは削除されています)では、resをモックし、jsonメソッドのモック実装のみを提供しています。これがテストに必要な唯一のメソッドです。

// SUT
kids.index = function (req, res) {
if (!req.user || !req.user._id) {
    res.json({
        err: "Invalid request."
    });
} else {
    // non-relevent code
}
};

// Unit test
var req, res, err, sentData;
describe('index', function () {

    beforeEach(function () {
        res = {
            json: function (resp) {
                err = resp.err;
                sentData = resp.kids;
            }
        };
    });
    it("should return error if no user passed in request", function () {
        req = {};

        kidsController.index(req, res);
        expect(err).to.equal("Invalid request.");

    });

    /// More tests....
})
4
Abhishek Jain

実際のreqおよびresオブジェクトを使用する場合は、実際のリクエストをサーバーに送信する必要があります。ただし、これは思っているよりもはるかに簡単です。特急 github repo には多くの例があります。以下は、req.routeのテストを示しています

var express = require('../')
  , request = require('./support/http');

describe('req', function(){
  describe('.route', function(){
    it('should be the executed Route', function(done){
      var app = express();

      app.get('/user/:id/edit', function(req, res){

        // test your controllers with req,res here (like below)

        req.route.method.should.equal('get');
        req.route.path.should.equal('/user/:id/edit');
        res.end();
      });

      request(app)
      .get('/user/12/edit')
      .expect(200, done);
    })
  })
})
4
zemirco