4

モジュールの依存関係の 1 つをスタブ化することにより、モジュールを単体テストしようとしています。UserManager

モジュールの簡略化されたバージョンは次のとおりです。

// CodeHandler
module.exports = function(UserManager) {
  return {
    oAuthCallback: function(req, res) {
      var incomingCode = req.query.code;
      var clientKey = req.query.key;
      UserManager.saveCode(clientKey, incomingCode)
        .then(function(){
          res.redirect('https://test.tes');
        }).catch(function(err){
          res.redirect('back');
        }
      );
    }
  };
};

解決済みの Promise を返すように返すUserManager のsaveCode関数をスタブしていますが、それが呼び出されたとき、残念ながらアサーションの時点ではまだ呼び出されていません。Promiseassertres.redirectres.redirect

単体テストの簡略版は次のとおりです。

// test
describe('CodeHandler', function() {
  var req = {
    query: {
      code: 'test-code',
      key: 'test-state'
    }
  };

  var res = {
    redirect: function() {}
  };

  var expectedUrl = 'https://test.tes';
  var ch;

  beforeEach(function() {
    sinon.stub(UserManager, 'saveCode').returns(
      new RSVP.Promise(function(resolve, reject){
        resolve();
      })
    );

    sinon.stub(res, 'redirect');

    ch = CodeHandler(UserManager);
  });

  afterEach(function() {
    UserManager.saveCode.restore();
    res.redirect.restore();
  });

  it('redirects to the expected URL', function(){
    ch.oAuthCallback(req, res);
    assert(res.redirect.calledWith(expectedUrl));
  })
});

テスト中のメソッドが同期的に動作するように、promise を適切にスタブするにはどうすればよいですか?

4

2 に答える 2

2

を使用して解決策を考え出しましたsinon-stub-promise

describe('CodeHandler', function() {
  var req = {
    query: {
      code: 'test-code',
      key: 'test-state'
    }
  };
  var ch;
  var promise;

  var res = {
    redirect: function() {}
  };

  beforeEach(function() {
    promise = sinon.stub(UserManager, 'saveCode').returnsPromise();
    ch = CodeHandler(UserManager);
    sinon.stub(res, 'redirect');
  });

  afterEach(function() {
    UserManager.saveCode.restore();
    res.redirect.restore();
  });

  describe('can save code', function() {
    var expectedUrl = 'https://test.tes';

    beforeEach(function() {
        promise.resolves();
    });

    it('redirects to the expected URL', function(){
      ch.oAuthCallback(req, res);
      assert(res.redirect.calledWith(expectedUrl));
    });
  });

  describe('can not save code', function() {
    var expectedUrl = 'back';

    beforeEach(function() {
        promise.rejects();
    });

    it('redirects to the expected URL', function(){
      ch.oAuthCallback(req, res);
      assert(res.redirect.calledWith(expectedUrl));
    })
  })
});

これは完全に機能します。

于 2015-09-29T07:36:25.617 に答える
1

最も簡単な方法は、実行順序が変更され、Mocha の組み込みのプロミス サポート (ジャスミンを使用している場合は jasmine-as-promised) が使用される可能性があるため、同期的に実行するようにスタブしないことです。

その理由は、次のような場合があるからです。

somePromise.then(function(){
    doB();
});
doA();

promise を実行順序を同期的に解決すると、プログラムの出力が変更され、テストの価値がなくなります。

逆に、テスト構文を使用できます。

describe("the test", () => { // use arrow functions, node has them and they're short
    it("does something", () => {
        return methodThatReturnsPromise().then(x => {
           // assert things about x, throws will be rejections here
           // which will cause a test failure, so can use `assert`
        });
    });
});

単一行にさらに軽い矢印構文を使用して、テストをさらに冗長にすることができます。

describe("the test", () => { // use arrow functions, node has them and they're short
  it("does something", () =>
      methodThatReturnsPromise().then(x => {
         // assert things about x, throws will be rejections here
         // which will cause a test failure, so can use `assert`
      });
  );
});

RSVP では、私の知る限り、スケジューラを設定できないため、とにかく同期的にテストすることはまったく不可能です。bluebird などの他のライブラリでは、自己責任で実行できますが、実行できるライブラリでも、おそらくそうではありません最高のアイデア。

于 2015-09-29T07:14:36.020 に答える