简体   繁体   English

与sinon和bluebird一起确定一个有希望的功能

[英]Stubbing a promisified function with sinon and bluebird

In the file I would like to test, I have the following code: 在我想测试的文件中,我有以下代码:

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

Now, in my tests, I want to make sure that httpGet was called once, and make sure the parameters are valid. 现在,在我的测试中,我想确保httpGet被调用一次,并确保参数有效。 Before being promisified, my test looked like this: 在被宣传之前,我的测试看起来像这样:

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);
    //...
});

Unfortunately this no longer works when request.get is promisified. 不幸的是,当request.get被宣传时,这不再有效。 I tried stubbing request.getAsync instead (since bluebird appends "Async" to promisified functions), but that does not work either. 我尝试了stubing request.getAsync(因为bluebird将“Async”附加到promisified函数),但这也不起作用。 Any ideas? 有任何想法吗?

Promise.promisify doesn't modify the object, it simply takes a function and returns a new function, it is completely unaware that the function even belongs to "request" . Promise.promisify不修改对象,它只需要一个函数并返回一个新函数,它完全没有意识到该函数甚至属于"request"

"Async" suffixed methods are added to the object when using promisify All 使用promisify All时,会向对象添加"Async"后缀方法

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();

Just for future reference I've solved this a bit differently, and I think a little cleaner. 仅供将来参考,我已经解决了这个问题,我认为有点清洁。 This is typescript, but basically the same thing. 这是打字稿,但基本上是一样的。

fileBeingTested.ts fileBeingTested.ts

import * as Bluebird from 'bluebird';
import * as needsPromise from 'needs-promise';

const methodAsync = Bluebird.promisify(needsPromise.method);

export function whatever() {
    methodAsync().then(...).catch(...);
}

test.spec.ts test.spec.ts

import * as needsPromise from 'needs-promise';
import * as sinon form 'sinon';

const methodStub = sinon.stub(needsPromise, method);
import { whatever } from './fileBeingTested';

Then you use the methodStub to manage what calls happen. 然后使用methodStub来管理发生的调用。 You can ignore that it's being promisified and just manage it's normal behavior. 你可以忽略它正在被宣传并且只是管理它的正常行为。 for example if you need it to error. 例如,如果你需要它错误。

methodStub.callsFake((arg, callback) => {
    callback({ error: 'Error' }, []);
});

The promisified version will throw the error and you'll get it in the catch. promisified版本将抛出错误,你将获得它。

Anyone coming across this. 有人遇到这个。 I have small utility func 我有小实用功能

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

In test 在测试中

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();

I was running into trouble testing this using tape and proxyquire . 我使用tapeproxyquire测试它时遇到了麻烦。 I'm not sure what pattern/framework people are using that allowed them to modify the required 'd request object directly as shown in the accepted answer. 我不确定人们使用哪种模式/框架允许他们直接修改required 'd request对象,如接受的答案中所示。 In my case, in the file I want to test I require('jsonFile') , then call bluebird.promisifyAll(jsonFile) . 在我的情况下,在我要测试的文件中我require('jsonFile') ,然后调用bluebird.promisifyAll(jsonFile) Under normal conditions this creates a readFileAsync method that I want to stub. 在正常情况下,这会创建一个我想要存根的readFileAsync方法。 However, if during testing I try to use proxyquire to pass in a stub, the call to promisifyAll overwrites my stub. 但是,如果在测试期间我尝试使用proxyquire传入存根,则调用promisifyAll覆盖我的存根。

I was able to fix this by also stubbing promisifyAll to be a no-op. 我能够通过将promisifyAll作为无操作来解决这个问题。 As shown this might be too coarse if you rely on some of the async methods to be created as-is. 如图所示,如果您依赖某些异步方法按原样创建,则可能过于粗糙。

core.js : 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 : 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.
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM