Sinon.stub 不存根私有方法
Sinon.stub not stubbing private method
在下面的模块中,我有一个创建临时目录的私有方法。我正在为这个模块 resize
编写测试,并希望将其私有方法 createTmpFile
和 return 存入一个假目录。我正在使用 sinon
来执行此操作,但仍在调用原始方法。
我试过用 var _private = require('../lib/modules/resizer')._private;
要求私有方法,然后用 tmpStub = sinon.stub(_private, 'createTmpFile', function () { return "/temporary/"});
在我的测试服中存根它
这不会引发任何错误,但仍会停止调用该函数。
如何用存根替换私有方法?
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");
});
});
问题是您存根 _private.createTmpFile
,其中包含对您的私有函数的 引用。您正在用存根替换引用,但这与对原始函数进行存根不同。
除非您在调整大小函数中实际使用 exports._private.createTmpFile
(这不是您想要的,因为 exports._private
仅存在于 test
环境中),否则您不会使用stub 而是实际函数(因为 createTmpFile
仍然指向原始函数)。
老实说,不确定是否有解决这个问题的干净方法。
感谢@robertklep 的评论,我决定再次用 proxyquire
解决这个问题,并得出以下通过我测试的解决方案。
我更改了设置,将 tmp.dirSync
添加到 proxyquire
并将其强制到 return 假目录。我还删除了临时目录的存根。
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/');
});
});
在下面的模块中,我有一个创建临时目录的私有方法。我正在为这个模块 resize
编写测试,并希望将其私有方法 createTmpFile
和 return 存入一个假目录。我正在使用 sinon
来执行此操作,但仍在调用原始方法。
我试过用 var _private = require('../lib/modules/resizer')._private;
要求私有方法,然后用 tmpStub = sinon.stub(_private, 'createTmpFile', function () { return "/temporary/"});
这不会引发任何错误,但仍会停止调用该函数。
如何用存根替换私有方法?
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");
});
});
问题是您存根 _private.createTmpFile
,其中包含对您的私有函数的 引用。您正在用存根替换引用,但这与对原始函数进行存根不同。
除非您在调整大小函数中实际使用 exports._private.createTmpFile
(这不是您想要的,因为 exports._private
仅存在于 test
环境中),否则您不会使用stub 而是实际函数(因为 createTmpFile
仍然指向原始函数)。
老实说,不确定是否有解决这个问题的干净方法。
感谢@robertklep 的评论,我决定再次用 proxyquire
解决这个问题,并得出以下通过我测试的解决方案。
我更改了设置,将 tmp.dirSync
添加到 proxyquire
并将其强制到 return 假目录。我还删除了临时目录的存根。
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/');
});
});