Reputation: 4228
In the following module I have a private method which creates a temporary directory. I am writing the tests for this module, resize
, and want to stub its private method createTmpFile
and return a fake directory. I am using sinon
to do this, but the original method is still being called.
I've tried requiring the private method with var _private = require('../lib/modules/resizer')._private;
and then stubbing it in my test suit with tmpStub = sinon.stub(_private, 'createTmpFile', function () { return "/temporary/"});
This throws no errors, but still stop the function from being called.
How can I replace the private method with the stub?
resize
'use strict';
// dependencies
var gm = require('gm').subClass({ imageMagick: true });
var tmp = require('tmp');
var async = require('async');
var resizer = {};
resizer.resize = function (path, sizesObj, callback) {
console.log(path);
var directory = createTmpFile();
var imgType = path.split(".").pop();
async.each(sizesObj, function (sizesObj, mapNext) {
gm(path)
.resize(sizesObj.width, sizesObj.height)
.write(directory + sizesObj.name + "." + imgType, function (err) {
if (err) {
mapNext(err);
}
mapNext();
});
}, function (err) {
if(err) {
callback(err);
} else {
callback(directory);
}
});
};
// This function creates a temporary directory to which we will save our files.
var createTmpFile = function () {
var tmpDir = tmp.dirSync(); //object
var tmpDirName = tmpDir.name + "/"; //path to directory
return tmpDirName;
};
module.exports = resizer;
if ( process.env.NODE_ENV === 'test') {
module.exports._private = {
createTmpFile: createTmpFile
}
};
test
describe("resizer when data is path", function () {
var testedModule, dir, sizesObj, tmpStub, writeStub250, writeStub500, writeStub650, writeStub750, callbackSpy, resizeStub, gmSubClassStub;
var _private = require('../lib/modules/resizer')._private;
console.log(_private.createTmpFile);
before(function () {
dir = "/tmp/rstest.png";
sizesObj = [
{name: "thumb", width: 250, height: 250},
{name: "small", width: 500, height: 500},
{name: "medium", width: 650, height: 650},
{name: "large", width: 750, height: 750}
];
writeStub250 = sinon.stub();
writeStub500 = sinon.stub();
writeStub650 = sinon.stub();
writeStub750 = sinon.stub();
resizeStub = sinon.stub();
tmpStub = sinon.stub(_private, 'createTmpFile', function () { return "/temporary/"});
gmSubClassStub = sinon.stub();
callbackSpy = sinon.spy();
testedModule = proxyquire('../lib/modules/resizer', {
'gm': {subClass: sinon.stub().returns(gmSubClassStub)}
});
});
after(function () {
_private.createTmpFile.restore();
});
it("resize image and write to path", function () {
resizeStub.withArgs(250, 250).returns({write:writeStub250});
resizeStub.withArgs(500, 500).returns({write:writeStub500});
resizeStub.withArgs(650, 650).returns({write:writeStub650});
resizeStub.withArgs(750, 750).returns({write:writeStub750});
// Stub is used when you just want to simulate a returned value
gmSubClassStub.withArgs(dir).returns({resize:resizeStub});
// Act - this calls the tested method
testedModule.resize(dir, sizesObj, function (err) {
callbackSpy.apply(null, arguments);
});
expect(writeStub250).has.been.called.and.calledWith("/temporary/thumb.png");
});
});
Upvotes: 1
Views: 3069
Reputation: 4228
So thanks to @robertklep comments, I decided to tackle the issue with proxyquire
once again and came to the following solution which passes my tests.
I have changed the set-up by adding tmp.dirSync
to proxyquire
and force it to return the fake directory. I also removed the stub for the temporary directory.
describe("resizer when data is path", function () {
var testedModule, dir, sizesObj, writeStub250, writeStub500, writeStub650, writeStub750, callbackSpy, resizeStub, gmSubClassStub;
before(function () {
dir = "/tmp/rstest.png";
sizesObj = [
{name: "thumb", width: 250, height: 250},
{name: "small", width: 500, height: 500},
{name: "medium", width: 650, height: 650},
{name: "large", width: 750, height: 750}
];
writeStub250 = sinon.stub();
writeStub500 = sinon.stub();
writeStub650 = sinon.stub();
writeStub750 = sinon.stub();
resizeStub = sinon.stub();
gmSubClassStub = sinon.stub();
callbackSpy = sinon.spy();
testedModule = proxyquire('../lib/modules/resizer', {
'gm': {subClass: sinon.stub().returns(gmSubClassStub)},
'tmp': {
'dirSync': function () {
return {
name: "/temporary"
}
}
}
});
});
it("resize image and write to path", function () {
resizeStub.withArgs(250, 250).returns({write:writeStub250});
resizeStub.withArgs(500, 500).returns({write:writeStub500});
resizeStub.withArgs(650, 650).returns({write:writeStub650});
resizeStub.withArgs(750, 750).returns({write:writeStub750});
// Stub is used when you just want to simulate a returned value
gmSubClassStub.withArgs(dir).returns({resize:resizeStub});
// Act - this calls the tested method
testedModule.resize(dir, sizesObj, function (err) {
callbackSpy.apply(null, arguments);
});
expect(writeStub250).has.been.called.and.calledWith("/temporary/thumb.png");
expect(writeStub500).has.been.called.and.calledWith("/temporary/small.png");
expect(writeStub650).has.been.called.and.calledWith("/temporary/medium.png");
expect(writeStub750).has.been.called.and.calledWith("/temporary/large.png");
});
it("calls callbackSpy", function () {
writeStub250.callsArgWith(1, null);
writeStub500.callsArgWith(1, null);
writeStub650.callsArgWith(1, null);
writeStub750.callsArgWith(1, null);
resizeStub.withArgs(250, 250).returns({write:writeStub250});
resizeStub.withArgs(500, 500).returns({write:writeStub500});
resizeStub.withArgs(650, 650).returns({write:writeStub650});
resizeStub.withArgs(750, 750).returns({write:writeStub750});
// Stub is used when you just want to simulate a returned value
gmSubClassStub.withArgs(dir).returns({resize:resizeStub});
// Act - this calls the tested method
testedModule.resize(dir, sizesObj, function (err) {
callbackSpy.apply(null, arguments);
});
expect(callbackSpy).has.been.called.and.calledWith('/temporary/');
});
});
Upvotes: 0
Reputation: 203231
The issue is that you're stubbing _private.createTmpFile
, which holds a reference to your private function. You're replacing the reference with the stub, but that's not the same as stubbing the original function.
Unless you actually use exports._private.createTmpFile
(which is not something you want because exports._private
only exists in the test
environment) in your resize function, you won't be using the stub but the actual function (because createTmpFile
still points to the original function).
Not sure if there's a clean way to solve this, to be honest.
Upvotes: 2