Обрезание многообещающей функции с синоном и синей птицей

В файле, который я хотел бы протестировать, у меня есть следующий код:

var httpGet = Promise.promisify(require("request").get);
httpGet(endpoint, {
    auth: {bearer: req.body.access_token},
    json: true
})
    .then(...)

Теперь, в моих тестах, я хочу убедиться, что HTTPGet был вызван один раз и убедитесь, что параметры действительны. Перед тем, как обещать, мой тест выглядит следующим образом:

beforeEach(function () {
    request.get = sinon.stub()
        .yields(null, null, {error: "test error", error_description: "fake google error."});
});

afterEach(function () {
    expect(request.get).to.have.been.calledOnce();
    var requestArgs = request.get.args[0];
    var uri = requestArgs[0];

    expect(uri).to.equal(endpoint);
    //...
});

К сожалению, это не работает, когда request.get является многообещающим. Вместо этого я попробовал наследовать request.getAsync(поскольку bluebird добавляет "Async" к многозначным функциям), но это тоже не работает. Любые идеи?

Ответ 1

Promise.promisify не изменяет объект, он просто берет функцию и возвращает новую функцию, она полностью не осознает, что функция даже принадлежит "request".

"Async" суффиксные методы добавляются к объекту при использовании promisify All

Promise.promisifyAll(require("request"));

request.getAsync = sinon.stub()
        .yields(null, null, {error: "test error", error_description: "fake google error."});

expect(request.getAsync).to.have.been.calledOnce();

Ответ 2

Любой, кто сталкивается с этим. У меня небольшая утилита func

function stubCBForPromisify(stub) {
  let cbFn = function() {
    let args = [...arguments];
    args.shift();
    return stub(...args);
  };
  return cbFn.bind(cbFn, () => ({}));
}

В тесте

var getStub = sinon.stub().yields(null, {error: "test error", error_description: "fake google error."})
sinon.stub(require("request"), 'get', stubCBForPromisify(getStub))
expect(getStub).to.have.been.calledOnce();

Ответ 3

Я столкнулся с проблемой тестирования с использованием tape и proxyquire. Я не уверен, какие люди используют шаблоны/рамки, которые позволяют им напрямую изменять объект required 'd request, как показано в принятом ответе. В моем случае, в файле я хочу проверить я require('jsonFile'), затем вызовите bluebird.promisifyAll(jsonFile). В обычных условиях это создает метод readFileAsync, который я хочу заглушить. Однако, если во время тестирования я пытаюсь использовать proxyquire для передачи в заглушку, вызов promisifyAll перезаписывает мой заглушка.

Я смог исправить это, также обрезая promisifyAll как no-op. Как показано, это может быть слишком грубым, если вы полагаетесь на некоторые из асинхронных методов, которые должны быть созданы как есть.

core.js:

var jsonFile = require('jsonfile');
var Promise = require('bluebird');
Promise.promisifyAll(jsonFile);

exports.getFile = function(path) {
  // I want to stub this method during tests. It is
  // created by promisifyAll
  return jsonFile.readFileAsync(path);
}

core-test.js:

var proxyquire = require('proxyquire');
var tape = require('tape');
var sinon = require('sinon');
require('sinon-as-promised');

tape('stub readFileAsync', function(t) {
  var core = proxyquire('./core', {
    'jsonfile': {
      readFileAsync: sinon.stub().resolves({})
    },
    'bluebird': { promisifyAll: function() {} }
  });
  // Now core.getFile() will use my stubbed function, and it
  // won't be overwritten by promisifyAll.
});