#node.js #sinon
#node.js #sinon
Вопрос:
Мне нужно заглушить метод sendMandrill объекта mh.
Смотрите мой тестируемый файл (mail.js ):
let MailHandler = require('../../modules/mail.handler.module');
...
let api = (router, parser) => {
let send = async (req, res, next) => {
let mh = new MailHandler();
mh.sendMandrill();
...
}
...
return router.post('/mail/send', parser.json(), send);
}
module.exports = api;
...
Мой тест (mail.spec.js ):
let stRequest = require('supertest');
let MailHandler = require('../../modules/mail.handler.module');
describe('my test', () => {
beforeEach(() => {
sinon.stub(MailHandler.prototype, 'sendMandrill', () => true);
})
it('stubs sendMandrill!', done => {
stRequest(app)
.post('/mail/send')
.end((err, resp) => {
done();
});
})
})
В настоящее время я получаю сообщение об ошибке ниже:
TypeError: Cannot stub non-existent own property sendMandrill
Добавление mail.handler.module — Смотрите ниже код MailHandler / sendMandrill:
module.exports = mailHandler;
function mailHandler() {
...
var mandrill = require('../modules/mandrill');
var handler = {
sendMandrill: sendMandrill,
...
};
return handler;
function sendMandrill() {
mandrill.messages.sendTemplate({
message: {...}
});
}
...
}
Комментарии:
1. Можете ли вы опубликовать фрагмент модуля обработки почты?
2. @WakeskaterX почему это имеет значение? Подумайте о MailHandler как об универсальном классе, который должен быть создан, а метод, который должен быть заглушен, находится в результирующем объекте.
3. Попробуйте изменить:
sinon.stub(MailHandler.prototype, 'sendMandrill', () => true);
на:sinon.stub(MailHandler, 'sendMandrill', () => true);
, чтобы узнать больше: medium.com/@alfasin/stubbing-with-sinon-4d6539caf3654. Спасибо @alfasin — к сожалению, я получаю ту же ошибку.
5. Это означает, что у
MailHandler
такой функции нет. Вы уверены, что импортируете его правильно? Попробуйте нажать на него с помощью отладчика, чтобы увидеть, какMailHandler
выглядит
Ответ №1:
Ваш текущий подход создает новый sendMandrill
для каждого экземпляра, созданного mailHandler
factory. На самом деле вы должны называть его без new let mh = mailHandler()
или даже лучше переименовать его в createMailHandler
, чтобы избежать неправильного использования.
Если вы хотите эффективно использовать наследование прототипов, вам нужно будет переписать, mailHandler
чтобы использовать фактическое использование this
вместо вновь созданного объекта.
var mandrill = require('../modules/mandrill');
module.exports = MailHandler;
function MailHandler() {
// use this instead of newly created object
this.foo = 'bar'
// avoid explicit return
// return handler;
}
// set methods to prototype
MailHandler.prototype.sendMandrill = function sendMandrill() {
// use this instead of handler here
mandrill.messages.sendTemplate({
message: {...}
});
}
Используя описанный выше подход, вы могли бы заглушить свойства прототипа с помощью sinon
и оправдать вызов конструктора с new
ключевым словом.
UPD
Если у вас нет контроля над mail.handler.module
, вы могли бы либо использовать rewire
модуль, который позволяет имитировать целые зависимости, либо выставлять MailHandler
как часть вашего api
модуля, чтобы сделать его инъекционным.
api.MailHandler = require('../../modules/mail.handler.module')
let mh = api.MailHandler();
А затем в тестах
let oldMailHandler;
beforeAll(() => { oldMailHandler = api.MailHandler})
afterAll(() => { api.MailHandler = oldMailHandler})
beforeEach(() => { api.MailHandler = function MockMailHandler() {} })
Комментарии:
1. Спасибо @Yuri Tarabanko. У меня нет контроля над mail.handler.module, поэтому я не могу там ничего изменить. Что мне нужно сделать, так это смоделировать зависимость, которую имеет функция, которую я должен протестировать («отправить»). «отправить» получает ссылку на объект, возвращаемый MailHandler() (новый экземпляр, если вызывается с помощью «new», или ссылку на существующий объект в противном случае, это не имеет значения). Я должен заглушить метод «sendMandrill» этого объекта. Вы хотите сказать, что единственный способ заглушить зависимости через sinon — это заглушить прототип?
2. @Miki Если вы не являетесь владельцем,
mailHandler
тогда я бы рекомендовал использоватьrewire
для макетирования всей зависимости. Или сделать его «вводимым», выполнивapi.ModuleHandler = require('../../modules/mail.handler.module')
и используяlet mh = api.ModuleHandler()
. тогда вы сможете внедрить любую реализацию в свои тесты.3. Хорошо, я сделал MailHandler инъекционным, как вы предложили, и теперь я могу его отключить.