Как заглушить требуемый конструктор nodejs с помощью 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: "[email protected]", first: "User"};
        const FROM = {email: "[email protected]", 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 указывает на функцию EmailTemplate без заглушки, определенную в beforeEach, а не на заглушку sinon, созданную в последней строке beforeEach. Однако если я оцениваю оператор require('email-templates').EmailTemplate, он правильно указывает на заглушку sinon. Я бы предпочел не менять свой код для вызова встроенного оператора require, например:

var template = new require('email-templates').EmailTemplate(__dirname + "/../emails/" + emailName);

Есть ли способ выполнить заглушку так, как я намереваюсь?


person Jared    schedule 24.12.2015    source источник


Ответы (1)


Вы можете внедрить свою зависимость при создании почтовой программы - 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);

Таким образом, ваше приложение (инфраструктура модульного тестирования или ваше фактическое/реальное приложение) будет определять тип почтовой программы, которая будет использоваться на протяжении всего жизненного цикла процесса.

person Tim    schedule 24.12.2015
comment
Я надеялся, что есть какой-то способ избежать встроенного требования, но это, по крайней мере, сохранит чистоту кода моего приложения. - person Jared; 26.12.2015