Mocking Mail API in Scala


Few days ago I was working on a Mailing API of my project and used javax-mail API but encountered the problem of how to write unit test cases for that without actually mocking the mail. I searched for it but was unable to find a proper documentation about mocking a mailing service. I tried few methods but some were confusing and some were really complex. Then I found Mock-JavaMail dependency which is quite easy to handle. Just add it and we are done.

I am using SBT project therefore using SBT dependency but you can find other relevant dependency on maven repository that will suit your project.

Let’s take an example:

First we add the required dependencies in our project.

libraryDependencies ++= Seq(
 "javax.mail" % "mail" % "1.4",
 "org.jvnet.mock-javamail" % "mock-javamail" % "1.9" % "test",
 "org.scalatest" % "scalatest_2.11" % "3.0.0-M15",
 "com.typesafe" % "config" % "1.3.0",
 "ch.qos.logback" % "logback-classic" % "1.1.7"
)

Then we create the necessary variables in our class.

final val EMPTY_STRING = ""
 val logger: Logger = LoggerFactory.getLogger(this.getClass())
 val conf = ConfigFactory.systemEnvironment()
 val senderEmail = try {
 conf.getString("email")
 } catch {
 case ex: Exception =>
 logger.error("Email key not set.\nPlease add email variable using -> export email=your_email@domain.com")
 EMPTY_STRING
 }

 val password = try {
 conf.getString("password")
 } catch {
 case ex: Exception =>
 logger.error("Password key not set.\nPlease add password variable using -> export password=your_password")
 EMPTY_STRING
 }
 val hostName = "smtp.gmail.com"

 val port = "587"

 val properties = new Properties
 properties.put("mail.smtp.port", port)
 properties.setProperty("mail.transport.protocol", "smtp")
 properties.setProperty("mail.smtp.starttls.enable", "true")
 properties.setProperty("mail.host", hostName)
 properties.setProperty("mail.user", senderEmail)
 properties.setProperty("mail.password", password)
 properties.setProperty("mail.smtp.auth", "true")

 val session = Session.getDefaultInstance(properties)

Here I am taking email and password from system environment variables, to use them you need to set them as following:

export email=your_email@domain.com

export password=your_password

Then we will use above variable in method mentioned below. I’ve written two methods, one for sending mail to multiple emails and one for a single email id and I’ve used method overloading functionality for that.

def sendMail(recipient: List[String], subject: String, content: String): Option[Int] = {
  try {
    val message = new MimeMessage(session)
    message.setFrom(new InternetAddress(senderEmail))
    val recipientAddress: Array[Address] = (recipient map { recipient => new InternetAddress(recipient) }).toArray
    message.addRecipients(Message.RecipientType.TO, recipientAddress)
    message.setSubject(subject)
    message.setHeader("Content-Type", "text/plain;")
    message.setContent(content, "text/html")
    val transport = session.getTransport("smtp")
    transport.connect(hostName, senderEmail, password)
    transport.sendMessage(message, message.getAllRecipients)
    logger.info("Email Sent!!")
    Some(recipient.size)
  }
  catch {
    case exception: Exception =>
      logger.error("Mail delivery failed. " + exception)
      None
  }
}

def sendMail(recipient: String, subject: String, content: String): Option[Int] = {
  try {
    val message = new MimeMessage(session)
    message.setFrom(new InternetAddress(senderEmail))
    message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient))
    message.setSubject(subject)
    message.setHeader("Content-Type", "text/plain;")
    message.setContent(content, "text/html")
    val transport = session.getTransport("smtp")
    transport.connect(hostName, senderEmail, password)
    transport.sendMessage(message, message.getAllRecipients)
    logger.info("Email Sent!!")
    Some(recipient.length)
  }
  catch {
    case exception: Exception =>
      logger.error("Mail delivery failed. " + exception)
      None
  }
}

Now let’s move to the real motive of this blog which is mocking mail.  I’ve used ScalaTest for testing my class. Now all you have to do is to write unit test cases and Mock-JavaMail will redirect all mails to in-memory .

Note: Mock-JavaMail will redirect all mails to in-memory so don’t forget to add scope as test otherwise you will not be able to send mail from your production code.

“groupId” % “artifactId” % “version” % “scope”

Unit Test Code:

package com.knoldus.email

import org.scalatest.FunSuite

class MailerAPITest extends FunSuite {

val emailAgent = new MailerAPI
val recipientList = List("prabhatkashyap33@gmail.com")
val subject = "This is my Subject"
val message = "This is my Message"

val recipient = "prabhatkashyap33@gmail.com"

test("Send Email to one address"){
val result = emailAgent.sendMail(recipient, subject, message)
assert(result.isDefined)
}

test("Send Email to multiple address"){
val result = emailAgent.sendMail(recipientList, subject, message)
assert(result.isDefined)
}

}

See complete source code on GitHub

About Prabhat Kashyap

Consultant(Engineer) at Knoldus Software LLP
This entry was posted in Scala, scalatest, Test, testing, tests and tagged , , . Bookmark the permalink.

One Response to Mocking Mail API in Scala

  1. Prabhat Kashyap says:

    Reblogged this on Prabhat Kashyap – Scala-Trek.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s