Как смоделировать доменные специфические замыкания в Споке
Я хотел бы протестировать контроллер Grails, который отправляет электронные письма с помощью плагина Grails Email. Я в недоумении, как именно издеваться sendMail
закрытие для взаимодействия для работы. Вот моя последняя версия тестового кода:
def 'controller should send a multipart email'() {
given: 'a mocked mailService'
controller.mailService = Mock(grails.plugin.mail.MailService)
controller.mailService.sendMail(*_) >> Mock(org.springframework.mail.MailMessage)
when:
controller.sendNow()
then:
1* _.multipart(true)
}
Код контроллера выглядит примерно так, как вы ожидаете, например:
def mailService
def sendNow() {
mailService.sendMail {
multipart true
to 'example@example.org'
from 'me@here.com'
subject 'a subject'
body 'a body'
}
}
Если я запускаю этот тест, я получаю 0 вызовов моего multipart
взаимодействие вместо 1. Вторая строка given:
блок кажется мне подозрительным, но если я пытаюсь издеваться Closure
вместо org.springframework.mail.MailMessage
мой тест вылетает Я должен также упомянуть, что сам контроллер работает, как и ожидалось (он не мог дождаться, когда я сначала выясню модульные тесты).
отредактированный
Ага, глядя на код свежим взглядом несколько часов спустя, я понимаю, почему вышеприведенный код не работает; для того, чтобы я поймал multipart
и другие вызовы DSL, я должен был бы смоделировать само замыкание, а не метод sendMail (и я не могу этого сделать, так как замыкание определено внутри самого контроллера). Что я, вероятно, могу сделать, это проверить аргументы sendMail
метод, чтобы увидеть все необходимое был передан в него.
4 ответа
Вы можете установить плагин greenMail и использовать его в интеграционном тесте:
С домашней страницы плагина greenmail:
import com.icegreen.greenmail.util.*
class GreenmailTests extends GroovyTestCase {
def mailService
def greenMail
void testSendMail() {
Map mail = [message:'hello world', from:'from@piragua.com', to:'to@piragua.com', subject:'subject']
mailService.sendMail {
to mail.to
from mail.from
subject mail.subject
body mail.message
}
assertEquals(1, greenMail.getReceivedMessages().length)
def message = greenMail.getReceivedMessages()[0]
assertEquals(mail.message, GreenMailUtil.getBody(message))
assertEquals(mail.from, GreenMailUtil.getAddressList(message.from))
assertEquals(mail.subject, message.subject)
}
void tearDown() {
greenMail.deleteAllMessages()
}
}
Я не эксперт по споку, но вы должны быть в состоянии перевести этот тест джунта на стиль спока.
Источник: http://grails.org/plugin/greenmail
Udpate, альтернатива по издевательству sendMail
Это ответ на обновление Грегора. На мой взгляд, вы должны будете смоделировать метод sendMail, и внутри этого метода есть заглушка, которая реализует различные свойства и методы, используемые в замыкании. Давайте назовем это оценщиком. Вы бы инициализировали делегат замыкания в valuatro и выполнили замыкание. Оценщик должен иметь утверждения. Вы видите, что я использую больше концепций Junit здесь. Я не знаю, как легко вы можете перевести это в понятия спока. Вы, вероятно, могли бы нам использовать средства проверки поведения спока.
class MailVerifier {
void multiPart(boolean v){
//...
}
void to(String address){
//...
}
boolean isVerified() {
//check internal state obtained by the appropriate invocation of the methods
}
}
def sendMail(Closure mailDefintion) {
def evaluator = createMailVerifier()
mailDefinition.delegate = evaluator
mailDefinition()
assert evaluator.verified
}
Мне удалось добиться этого в Споке с помощью следующего:
def messageBuilder
def bodyParams
def setup(){
def mockMailService = new MockFor(MailService)
mockMailService.ignore.sendMail{ callable ->
messageBuilder = new MailMessageBuilder(null, new ConfigObject())
messageBuilder.metaClass.body = { Map params ->
bodyParams = params
}
callable.delegate = messageBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.call()
}
service.mailService = mockMailService.proxyInstance()
}
И пример теста:
def "sendEmailReceipt_passesCorrectParams"(){
when:
def receiptItems = [] << [item: "item1", price: 100]
service.sendEmailReceipt(receiptItems, "some@email.com")
then:
messageBuilder.message.to[0] == "some@email.com"
messageBuilder.message.subject == "My subject"
bodyParams.view == "/mailtemplates/emailReceipt"
bodyParams.model.receiptItems == data
}
def mailService = Mock(MailService)
mockMailService.metaClass.sendMail = { ... your logic ... }
controller.mailService = mailService
Взгляните на тесты плагинов здесь: тест интеграции плагинов и здесь: модульный тест плагинов. По моему мнению, вам будет трудно высмеять все зависимости MailService - фабрику и сборщик, который создает ваше почтовое сообщение. Я бы закончил с тестированием, только если мой контроллер называется sendNow.
редактировать
Я нашел этот ответ. В соответствии с этим вы можете попробовать:
def 'controller should send a multipart email'() {
given: 'a mocked mailService'
def mockMailService = new Object()
def mockMessageBuilder = Mock(MessageBuilder)
mockMailService.metaClass.sendMail = { callable ->
callable.delegate = mockMessageBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.call()
}
controller.mailService = mockMailService
when:
controller.sendNow()
then:
1* mockMessageBuilder.multipart(true)
}