Unit testing Java mail code

Posted on by

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!

16 thoughts on “Unit testing Java mail code

  1. Very useful article, thank you! I was searching on how to unit test a JavaMail based application and found a couple of quite “lowtrust” articles before I came here and immediately felt: This is it! My feeling proved right when trying it.

  2. Generally I do not leaen post on blogs, but I would like to say
    that this write-up very compeled me to check out and ddo so!
    Your writing taste hhas been amazed me. Thanks, very nice post.

  3. whoah this blog is fantastic i love reading your posts.
    Stay up the god work! You know, a lot of people are searchiing round
    forr this info, you can help them greatly.

  4. Hey Your web site runs up really slow for
    me, I don’t know who’s issue is that but facebook starts
    relatively fast. However , thank you for putting up excellent blog post.
    Everybody who found this great site must have noticed this
    article totally very helpful. I really hope I will
    be able to find more remarkable things and I really should compliment your site simply by saying
    you have done awesome writing. Just after viewing your blog post, I have bookmarked your webblog.

  5. Excellent beat ! I wish to apprentice whilst you amend
    your website, how can i subscribe for a weblog website?
    The account aided me a acceptable deal. I were iny bit familiar oof this your
    broadcast provided brilliant transparent idea

  6. Unquestionably consider that which you stated. Your favourite reason seemed to
    be on the net the easiest factor to be mindful of. I say to you,
    I definitely get annoyed at thhe same time as other people consider issues that they just do noot recognise about.
    You controlled to hit the nail upon the highest ass smsrtly as defined out the whole thing with no need siode effect , other people could take a signal.

    Will probably bbe again tto get more. Thanhk you

  7. Amazing! This blo looks just like my old
    one! It’s on a entirely different subject but it has pretty much
    the same page layout and design. Great choice of colors!

  8. My spouse and I stumbled over here from a different page and thought I might
    as well check things out. I like what I see so now i
    am following you. Look forward to going over your web page
    for a second time.

  9. Fastidious replies in return of this difficulty with solid arguments and describing everything on the topic
    of that.

  10. Hello there Your entire web site runs up seriously slow for me, I’m not sure who’s issue is that however twitter and facebook starts up extremely fast.
    Anyway, Thanks for placing an extraordinarily fantastic blog post.
    Everyone who actually visited this web site really should
    have found this short article seriously very helpful.
    I ought to tell you that you have done wonderful work with
    this and expect to check out many more awesome content through you.
    I now have you saved to my bookmarks to look at blog you
    publish.

  11. Hey Your entire web page runs up incredibly slow in my situation, I not really know who’s problem is that however
    , facebook starts up fairly quick. Well, I’d like to say thanks for putting beautiful
    content. I do believe it really has already been honestly helpful
    to user who seem to click here. This one is without a doubt great what you actually have done and would
    like to see even more posts from your website.
    Immediately after taking a look at the content, I’ve book-marked
    your web page.

  12. The mentioned method in the article: waitForIncomingMail does not exist. It is waitForIncomingEmail. At least in 1.4.1 of greenmail.

Leave a Reply

Your email address will not be published. Required fields are marked *


*