Tag Archives: email

Unit testing Java mail code

GreenMail is a mail testing tool that has been around for a long time, but I’m surprised by how few people actually know about it.  I’ve found it very useful on many occasions for testing my mail sending code, both in integration/acceptance style tests, and also in unit tests.  In this post I’m going to give a short introduction to using GreenMail with JUnit, and then I’ll add a number of advanced tips that I’ve learned from my experience to help you effectively test your code. For my examples I’m going to be testing a class that I’ve written called MailSender.

Using GreenMail with JUnit

GreenMail starts as a server listening on ports for SMTP/POP/IMAP connections, these are naturally handled by background threads. In order to use GreenMail in unit testing, you need to start it up before each test and shut it down again after.  GreenMail is incredibly fast to startup and shutdown so there’s no worries about performance of your tests here.

private static final int SMTP_TEST_PORT = 3025;
private GreenMail greenMail;
private MailSender mailSender;

@Before
public void setUp() throws Exception {
    greenMail = new GreenMail(new ServerSetup(SMTP_TEST_PORT, null, "smtp"));
    greenMail.start();
    mailSender = new MailSender("localhost", SMTP_TEST_PORT);
}
@After
public void tearDown() throws Exception {
    greenMail.stop();
}

Now I’m ready to write my tests.  Here’s an example test:

@Test
public void sendShouldSetTheRightText() throws Exception {
    mailSender.send("Hello World!");
    assertThat((String) greenMail.getReceivedMessages()[0].getContent(),
        equalTo("Hello World!"));
}

The getReceivedMessages() method returns an array of javax.mail.internet.MimeMessage, each of which contains all the mime information from each sent email. Note that in this case, I’m assuming that everything is happening synchronously, which it is. Sometimes though (particularly in integration tests) mail is put onto a queue and sent asynchronously. GreenMail offers a handy method for dealing with this, waitForIncomingMail(), which accepts a timeout and a number of emails you want to wait for, and returns a boolean letting you know whether the mail you were waiting for arrived.

Selecting a good port

The above code works fine if you know that port 3025 is free. But you don’t always know that, particularly if you’re running your tests on a CI server, that CI server may be running another job that is trying to use the same port. I’ve found the following code useful for picking a good port:

private static int findAvailablePort(int min, int max) {
    for (int port = min; port < max; port++) {
        try {
            new ServerSocket(port).close();
            return port;
        } catch (IOException e) {
            // Must already be taken
        }
    }
    throw new IllegalStateException("Could not find available port in range "
            + min + " to " + max);
}

I can now specify a range of ports and my code will choose one that is free.

Checking who the mail was actually sent to

As you know, mime headers can often lie. Just like the header on a letter might be different to the name on the envelope, the To header set in a mime header might be different to that sent to the SMTP server. This is also known as bccing, and it may be the case that your code uses this. If that is the case, you can’t run assertions on the To field to ensure that the email was actually sent to the right person (although, due to this bug in GreenMail you actually can, but you can’t know whether they weren’t added to the To header by your code). However, GreenMail provides a way to get mail that was actually delivered to a particular user. The first thing you need to do is get a reference to the object for that user, this is most easily done by setting their password, I do this in the setUp() method:

greenMailUser = greenMail.setUser("recipient@example.org", null);

Now when it comes to writing my tests, I can look up that users inbox and find the messages that are in it:

MailFolder inbox = greenMail.getManagers().getImapHostManager().getInbox(greenMailUser);
List<StoredMessage> messages = inbox.getMessages();
if (!messages.isEmpty()) {
    messages.get(0).getMimeMessage();
    ...
} else {
    fail("No email for user arrived")
}

Checking the envelope from address

A similar issue to bccing is telling the SMTP server that you’re sending from a different email address to the email address you put in the mime headers. This is also known as the envelope from or bounce address, and it’s typically used for error reporting, for example, to tell the sender that the mailbox doesn’t exist. This can be set in Java by creating an instance of com.sun.mail.smtp.SMTPMessage instead of MimeMessage and setting it using the setEnvelopeFrom() method. This is not a mime header that we can just check, however SMTP servers are required to put this address into the email in the mime header Return-Path. GreenMail does this to. So to assert that the correct envelope from address was set:

String returnPathHeader = message.getHeader("Return-Path", ",");
assertThat(returnPathHeader, notNullValue());
InternetAddress returnPath = InternetAddress.parse(returnPathHeader)[0];
assertThat(returnPath.getAddress(), equalTo("bounce-notify@example.org"));

That concludes my post on using GreenMail, if you have any other helpful tips for using GreenMail, please share them as comments here!