4

我正在尝试通过存根模块的依赖项来对模块进行单元测试,在这种情况下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');
        }
      );
    }
  };
};

我正在对 UserManager 的saveCode函数进行存根,该函数返回一个Promise这样的函数,它返回一个已解决的 Promise,但是当我被调用时,唉,在断言assert时还没有被调用。res.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));
  })
});

如何正确地存根承诺,以便被测方法同步运行?

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 内置的 Promise 支持(如果使用 jasmine,则为 jasmine-as-promised)。

原因可能是这样的情况:

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

如果您导致承诺同步解决执行顺序 - 因此程序的输出会发生变化,从而使测试变得毫无价值。

相反,您可以使用测试语法:

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 回答