Open drhumlen opened 9 years ago
Hello,
If you are using Play 2.4 then you can use dependency injection to provide a mock of MailerClient to your UserModule.
With Guice (runtime dependency injection):
val application = new GuiceApplicationBuilder()
.overrides(bind[MailerClient].to[MockMailerClient])
.build
https://www.playframework.com/documentation/2.4.x/ScalaTestingWithGuice
You can create a mock of MailerClient using Mockito or ScalaMock. Then you can assert that sendEmail
method was called with the expected Email
object, something like:
"A verification email is sent when the user has registered" in new WithApplication(application) {
UserModule.registerNewUser(name = "Frodo", email = "frodo@example.com")
Mockito.verify(UserModule.mailerClient).sendEmail(Email(
subject = "Welcome Frodo",
recipient = "frodo@example.com",
bodyText = "Here is your verifcation link: ______"
))
}
Also, mock-mode must be on when running your test suite, right? (play.mailer.mock = yes)
No play.mailer.mock
should be considered deprecated because you can now choose to inject a MockMailer with the qualifier "mock", see: https://github.com/playframework/play-mailer/blob/master/src/main/scala/play/api/libs/mailer/MailerPlugin.scala#L25
Also in your test you want to do assertion. The MockMailer
in play-mailer will just prints out the email data, see: https://github.com/playframework/play-mailer/blob/master/src/main/scala/play/api/libs/mailer/MailerPlugin.scala#L248
I'm sorry if this is asked before, but I couldn't find anything in the manual or from searching.
Don't apologize this is a very good question and I need to add documentation about testing. If something is not clear, feel free to ask again.
Thanks for swift reply :)
Unfortunately, Play's support for dependency injection was a little too late for our project :( . We're already 2.5 years into a quite big play-project, so we don't have time to rewrite everything to use dependency injection.
I would go for the solution you suggested above though when/if we start another play-project, but for now I made a small wrapper to work with our existing code:
import play.api.Play.current
import play.api.libs.mailer.{Email, MailerPlugin}
object Mailer {
private var capturedEmails: List[Email] = Nil
private var captureMode = false
def send(email: Email): Unit = {
if (captureMode == true) {
capturedEmails = email :: capturedEmails
} else {
MailerPlugin.send(email)
}
}
def captureMailsSentIn(blk: => Unit): List[Email] = {
captureMode = true
capturedEmails = Nil
blk
captureMode = false
capturedEmails
}
}
It's not exactly 100% functional, but it does what must be done in order to test it. If anybody else is in the same situation as us, it's probably a good idea to set play.mailer.mock=yes
since all code that is not run directly inside captureMailsSentIn
will still send emails around to god-knows-who.
Anyway, just thought I'd post this here in case someone else has painted themselves into a DI-less-corner like we have. Here's how we use it in one of our tests:
"a login-token is sent on email when registering" in {
val capturedEmails = Mailer.captureMailsSentIn {
Helpers.reqTest(
routes.ApplicantController.registerForeign(),
input = ForeignRegistrationFormData(
name = "Frodo",
birthDate = DateTime.now,
email = "frodo@example.com",
mobile = "12345678",
gender = Gender.Male
),
output = Json.obj("id" -> JsonMatcher.___anyString),
expectedStatusCode = CREATED
)
}
capturedEmails must haveLength(1)
val mail = capturedEmails(0)
mail.to === Seq("frodo@example.com")
mail.bodyText.get must be containing "http://localhost:9000/api/verify?code="
}
(Edit: remember to replace every usage of the singleton MailerPlugin.send
with Mailer.send
)
Hello @drhumlen, thanks for sharing your findings. I think you can also use ScalaMock because its allow to mock singleton and companion objects. So I think you can do something like:
val mailerPluginMock = mockObject(MailerPlugin)
UserModule.registerNewUser(name = "Frodo", email = "frodo@example.com")
val expectedEmail = Email(
subject = "Welcome Frodo",
recipient = "frodo@example.com",
bodyText = "Here is your verifcation link: ______"
)
mailerPluginMock.expects.sendEmail(expectedEmail)
The benefit is that you don't have to modify your code.
Hi all -- placebocurejava has been banned from playframework, as he was posting on the google group with stolen credentials. His comments have been removed. Please see https://groups.google.com/forum/#!topic/play-framework/5FCht0abCI4 for more information.
@drhumlen maybe you can use something like this https://github.com/mailhog/MailHog have it as docker and use its API to do your test.
Is there a way to test that an email is sent, and check the contents of it? Would be nice to do something like:
Or perhaps something more "functional"/safe:
Is it possible to do something like this at all right now? ie. Setting the plugin in testing/capture mode so that we can verify: (1) that en email is sent, (2) verify the contents of it.
Also, mock-mode must be on when running your test suite, right? (
play.mailer.mock = yes
)I'm sorry if this is asked before, but I couldn't find anything in the manual or from searching.