【问题标题】:How can I stub a Promise such that my test can be run synchronously?如何存根 Promise 以便我的测试可以同步运行?
【发布时间】:2015-12-26 13:12:31
【问题描述】:

我正在尝试通过存根模块的依赖项来对模块进行单元测试,在本例中为 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.redirect 已被调用,唉,在断言时 @987654327 @ 尚未被调用。

单元测试的简化版本是:

// 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 以使被测方法同步运行?

【问题讨论】:

  • 你使用什么测试框架?它没有异步支持吗?
  • 使用mocha,是的,它支持异步,但在这种情况下这对我没有帮助。正在测试的不是 Promise,而是对 Promise 的结果做出反应的代码。
  • res 未在您的测试代码中定义。如果它是一个真正的函数,您可以在其中调用 done(作为测试函数的输入)
  • 抱歉,你是对的,我忘记在我实际测试的这段摘录中定义res。为了完整起见,我会修复它。

标签: node.js unit-testing promise stubbing rsvp-promise


【解决方案1】:

我已经使用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));
    })
  })
});

这很好用。

【讨论】:

    【解决方案2】:

    嗯,最简单的事情是 将其存根以同步运行,因为这可能会改变执行顺序并使用 Mocha 内置的 Promise 支持(如果使用 jasmine,则为 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)允许您自行承担风险,但即使在允许您这样做的库中这可能不是最好的主意。

    【讨论】:

    • 被测试的不是 Promise 本身,而是响应被测 Promise 的代码。
    • @DaveSag 那么该代码也应该返回一个承诺(在 UserManager.saveCode 之前放置 return),以便您可以对其进行测试,然后使用异步语法。
    猜你喜欢
    • 2022-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    • 2021-09-16
    • 2019-08-16
    相关资源
    最近更新 更多