如何使用 sinon 存根 nodejs "required" 构造函数?
How to stub a nodejs "required" constructor using sinon?
我正在为使用 email-templates
模块的方法编写单元测试,如下所示:
var EmailTemplate = require('email-templates').EmailTemplate;
module.exports = {
sendTemplateEmail: function (emailName, data, subject, to, from) {
var template = new EmailTemplate(__dirname + "/../emails/" + emailName);
data.from = FROM;
data.host = config.host;
return template.render(data)
.then(function (result) {
return mailer.sendEmail(subject, to, from, result.html, result.text);
})
.then(function () {
log.info(util.format("Sent %s email to %s. data=%s", emailName, to, JSON.stringify(data)));
return Promise.resolve();
})
.catch(function (err) {
return Promise.reject(new InternalError(err, "Error sending %s email to %s. data=%s", emailName, to, JSON.stringify(data)));
});
}
};
单元测试如下所示:
var assert = require("assert"),
sinon = require("sinon"),
Promise = require("bluebird"),
proxyquire = require("proxyquire");
describe('mailer#sendTemplateEmail', function () {
var templates,
template;
beforeEach(function() {
templates = {
EmailTemplate: function(path) {}
};
template = {
render: function(data) {}
};
sinon.stub(templates, "EmailTemplate").returns(template);
});
it("should reject immediately if template.render fails", function () {
const TO = {email: "user1@example.com", first: "User"};
const FROM = {email: "user2@example.com", first: "User"};
const EMAIL_NAME = "results";
const SUBJECT = "Results are in!";
const DATA = {
week: 10,
season: "2015"
};
var err = new Error("error");
var mailer = proxyquire("../src/mailer", {
"email-templates": templates
});
sinon.stub(template, "render").returns(Promise.reject(err));
return mailer.sendTemplateEmail(EMAIL_NAME, DATA, SUBJECT, TO, FROM)
.then(function () {
assert.fail("Expected a rejected promise.");
})
.catch(function (err) {
assert(err.message === "error");
assert(mailer.sendEmail.notCalled);
});
});
};
我遇到的问题是 sendTemplateEmail
函数的第一行,它实例化了一个新的 EmailTemplate
对象。被调用的 EmailTemplate
构造函数指向 beforeEach
中定义的非存根 EmailTemplate
函数,而不是 beforeEach
最后一行创建的 sinon 存根。但是,如果我评估 require('email-templates').EmailTemplate
语句,它会正确地指向 sinon 存根。我宁愿不必更改我的代码来调用内联的 require 语句,如:
var template = new require('email-templates').EmailTemplate(__dirname + "/../emails/" + emailName);
有什么方法可以按照我想要的方式完成存根吗?
您可以在构建邮件程序时注入依赖项 - exp:
function mailer(options) {
options = options || {};
this.email_template = options.email_template;
}
然后在 sendTemplateEmail 函数中 - 使用 email_template 成员。
另外 - 不确定你的邮件程序代码 - 但如果你需要你的邮件程序在你的代码中充当单例(而且现在还没有) - 你可以将它添加到你的邮件程序中:
module.exports = {
getInstance: function(emailTemplate) {
if(this.instance === null){
this.instance = new mailer(emailTemplate);
}
return this.instance;
}
}
然后当你需要你的邮件程序时,你可以使用语法:
var template = new require('email-templates').EmailTemplate(__dirname + "/../emails/" + emailName);
var mail = mailer.getInstance(template);
这样您的应用程序(单元测试框架或您的 actual/real-world 应用程序)将确定将在进程的生命周期内使用的邮件类型。
我正在为使用 email-templates
模块的方法编写单元测试,如下所示:
var EmailTemplate = require('email-templates').EmailTemplate;
module.exports = {
sendTemplateEmail: function (emailName, data, subject, to, from) {
var template = new EmailTemplate(__dirname + "/../emails/" + emailName);
data.from = FROM;
data.host = config.host;
return template.render(data)
.then(function (result) {
return mailer.sendEmail(subject, to, from, result.html, result.text);
})
.then(function () {
log.info(util.format("Sent %s email to %s. data=%s", emailName, to, JSON.stringify(data)));
return Promise.resolve();
})
.catch(function (err) {
return Promise.reject(new InternalError(err, "Error sending %s email to %s. data=%s", emailName, to, JSON.stringify(data)));
});
}
};
单元测试如下所示:
var assert = require("assert"),
sinon = require("sinon"),
Promise = require("bluebird"),
proxyquire = require("proxyquire");
describe('mailer#sendTemplateEmail', function () {
var templates,
template;
beforeEach(function() {
templates = {
EmailTemplate: function(path) {}
};
template = {
render: function(data) {}
};
sinon.stub(templates, "EmailTemplate").returns(template);
});
it("should reject immediately if template.render fails", function () {
const TO = {email: "user1@example.com", first: "User"};
const FROM = {email: "user2@example.com", first: "User"};
const EMAIL_NAME = "results";
const SUBJECT = "Results are in!";
const DATA = {
week: 10,
season: "2015"
};
var err = new Error("error");
var mailer = proxyquire("../src/mailer", {
"email-templates": templates
});
sinon.stub(template, "render").returns(Promise.reject(err));
return mailer.sendTemplateEmail(EMAIL_NAME, DATA, SUBJECT, TO, FROM)
.then(function () {
assert.fail("Expected a rejected promise.");
})
.catch(function (err) {
assert(err.message === "error");
assert(mailer.sendEmail.notCalled);
});
});
};
我遇到的问题是 sendTemplateEmail
函数的第一行,它实例化了一个新的 EmailTemplate
对象。被调用的 EmailTemplate
构造函数指向 beforeEach
中定义的非存根 EmailTemplate
函数,而不是 beforeEach
最后一行创建的 sinon 存根。但是,如果我评估 require('email-templates').EmailTemplate
语句,它会正确地指向 sinon 存根。我宁愿不必更改我的代码来调用内联的 require 语句,如:
var template = new require('email-templates').EmailTemplate(__dirname + "/../emails/" + emailName);
有什么方法可以按照我想要的方式完成存根吗?
您可以在构建邮件程序时注入依赖项 - exp:
function mailer(options) {
options = options || {};
this.email_template = options.email_template;
}
然后在 sendTemplateEmail 函数中 - 使用 email_template 成员。
另外 - 不确定你的邮件程序代码 - 但如果你需要你的邮件程序在你的代码中充当单例(而且现在还没有) - 你可以将它添加到你的邮件程序中:
module.exports = {
getInstance: function(emailTemplate) {
if(this.instance === null){
this.instance = new mailer(emailTemplate);
}
return this.instance;
}
}
然后当你需要你的邮件程序时,你可以使用语法:
var template = new require('email-templates').EmailTemplate(__dirname + "/../emails/" + emailName);
var mail = mailer.getInstance(template);
这样您的应用程序(单元测试框架或您的 actual/real-world 应用程序)将确定将在进程的生命周期内使用的邮件类型。