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. 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. I tried stubbing request.getAsync instead (since bluebird appends "Async" to promisified functions), but that does not work either. Any ideas?
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. 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. I tried stubbing request.getAsync instead (since bluebird appends "Async" to promisified functions), but that does not work either. Any ideas?
Share Improve this question asked Jan 30, 2015 at 14:31 NepoxxNepoxx 5,3588 gold badges46 silver badges63 bronze badges4 Answers
Reset to default 5Promise.promisify doesn't modify the object, it simply takes a function and returns a new function, it is pletely unaware that the function even belongs to "request"
.
"Async"
suffixed methods are added to the object when using 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();
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
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
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. 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.
Anyone ing 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
. 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. In my case, in the file I want to test I require('jsonFile')
, then call bluebird.promisifyAll(jsonFile)
. Under normal conditions this creates a readFileAsync
method that I want to stub. However, if during testing I try to use proxyquire
to pass in a stub, the call to promisifyAll
overwrites my stub.
I was able to fix this by also stubbing promisifyAll
to be a no-op. As shown this might be too coarse if you rely on some of the async methods to be created as-is.
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.
});