Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing that an email is sent #73

Open
drhumlen opened this issue Sep 23, 2015 · 6 comments
Open

Testing that an email is sent #73

drhumlen opened this issue Sep 23, 2015 · 6 comments

Comments

@drhumlen
Copy link

Is there a way to test that an email is sent, and check the contents of it? Would be nice to do something like:

"A verification email is sent when the user has registered" in {
  MailPlugin.startCapturing()
  UserModule.registerNewUser(name = "Frodo", email = "[email protected]")
  val emails: List[Email] = MailPlugin.stopCapturingAndCollectResults()

  emails mustEqual List(
    Email(
      subject = "Welcome Frodo",
      recipient = "[email protected]",
      bodyText = "Here is your verifcation link: ______"
    )
  )
}

Or perhaps something more "functional"/safe:

"A verification email is sent when the user has registered" in {
  val emails: List[Email] = MailPlugin.captureMailsSentInBlock {
    UserModule.registerNewUser(name = "Frodo", email = "[email protected]")
  }

  emails mustEqual List(
    Email(
      subject = "Welcome Frodo",
      recipient = "[email protected]",
      bodyText = "Here is your verifcation link: ______"
    )
  )
}

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.

@ggrossetie
Copy link
Member

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 = "[email protected]")
  Mockito.verify(UserModule.mailerClient).sendEmail(Email(
      subject = "Welcome Frodo",
      recipient = "[email protected]",
      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.

@drhumlen
Copy link
Author

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 = "[email protected]",
            mobile = "12345678",
            gender = Gender.Male
          ),
          output = Json.obj("id" -> JsonMatcher.___anyString),
          expectedStatusCode = CREATED
        )
      }

      capturedEmails must haveLength(1)
      val mail = capturedEmails(0)

      mail.to === Seq("[email protected]")
      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)

@ggrossetie
Copy link
Member

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 = "[email protected]")
val expectedEmail = Email(
  subject = "Welcome Frodo",
  recipient = "[email protected]",
  bodyText = "Here is your verifcation link: ______"
)
mailerPluginMock.expects.sendEmail(expectedEmail)

The benefit is that you don't have to modify your code.

@marcospereira
Copy link
Member

Hey,

The way I would recommend that is to have a separated configuration for your test environment, having just the email settings change, and then use something like Wiser or GreenMail which are both made to test email sending.

@wsargent
Copy link
Member

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.

@threeel
Copy link

threeel commented Sep 19, 2019

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants