Having written some e-mail handling, sending or receiving library you’d like to run some integration tests against a real mail server?
Then GreenMail might help you out here .. the framework is not quite new on the market but it is really easy to setup IMAP,SMTP or POP3 services with it and it comes with some helpful libraries making your life a bit easier here.
That’s why I’ve come to the idea to share some examples for setting up different server instances, creating user accounts and – at last – fetching and validating e-mails…
Adding GreenMail to your Project
Essentially there is just one dependency needed to integrate GreenMail into your existing project .. just add this Maven snippet or the Gradle build file to your project’s pom.xml or project directory …
Maven
Just add the following dependencies to your pom.xml – for my full project descriptor please take a look at the tutorial sources at GitHub.
<dependencies>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.5</version>
</dependency>
</dependencies>
Gradle
For the fellow Maven haters here’s the gradlish variant as build.gradle
apply plugin: 'java'
apply plugin: 'maven'
group = 'com.hascode.tutorial'
version = '0.0.1'
repositories {
mavenCentral()
}
configurations {
provided
testCompile.extendsFrom provided
compile.transitive = true
}
dependencies {
compile(
[group: 'javax.mail', name : 'mail', version : '1.4.5'],
[group: 'org.slf4j', name : 'slf4j-api', version : '1.6.6'],
[group: 'ch.qos.logback', name : 'logback-core', version : '1.0.6']
)
testCompile(
[group: 'com.icegreen', name : 'greenmail', version : '1.3'],
[group: 'junit', name : 'junit', version : '4.10'],
[group: 'org.hamcrest', name : 'hamcrest-all', version : '1.1']
)
}
sourceSets.main.compileClasspath += configurations.provided
Using GreenMail
Now to some examples .. to keep it pure I am going to implement the component to be tested using the well-known but imho ugly javax.mail API and on the other side the GreenMail server with a specific setup.
As you might notice there are several possible default configurations available e.g. ServerSetup.IMAP creates an IMAP server, ServerSetup.SMTP creates and SMTP server .. if you need both, just add an array of ServerSetup as constructor parameter when you create a new GreenMail server instance .. e.g. new GreenMail(new ServerSetup[]\{ServerSetup.IMAP, ServerSetup.SMTP}) . If you need everything from POP3 to SMTPS then just use ServerSetup.ALL and enjoy.
Per default, GreenMail uses the following ports:
-
smtp 25
-
smtps 465
-
pop3 110
-
pop3s 995
-
imap 143
-
imaps 993
It might happen that these ports are already used on your system or you’re working on an *nix like operating system that reserves the ports upto 1024 for system services and therefore only root is allowed to use them.
In this case you should – and I am doing this in the following examples – consider using GreenMail’s ServerSetupTest instead of ServerSetup .. it simply adds 3000 to the default ports so that the ports used are now:
-
smtp 3025
-
smtps 3465
-
pop3 3110
-
pop3s 3995
-
imap 3143
-
imaps 3993
If you somehow need to use another port simply pass a custom ServerSetup like in the following example where I’m creating a setup for SMTP on port 6666: new ServerSetup(6666, null, “smtp”)
POP3
In the following example we’re first creating a user account on the mail-server, we’re saving an e-mail on the server and finally an e-mail message is pulled from the GreenMail server using POP3.
package it;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.user.UserException;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetupTest;
public class Pop3IT {
private static final String USER_PASSWORD = "abcdef123";
private static final String USER_NAME = "hascode";
private static final String EMAIL_USER_ADDRESS = "hascode@localhost";
private static final String EMAIL_TO = "someone@localhost.com";
private static final String EMAIL_SUBJECT = "Test E-Mail";
private static final String EMAIL_TEXT = "This is a test e-mail.";
private static final String LOCALHOST = "127.0.0.1";
private GreenMail mailServer;
@Before
public void setUp() {
mailServer = new GreenMail(ServerSetupTest.POP3);
mailServer.start();
}
@After
public void tearDown() {
mailServer.stop();
}
@Test
public void getMails() throws IOException, MessagingException,
UserException, InterruptedException {
// create user on mail server
GreenMailUser user = mailServer.setUser(EMAIL_USER_ADDRESS, USER_NAME,
USER_PASSWORD);
// create an e-mail message using javax.mail ..
MimeMessage message = new MimeMessage((Session) null);
message.setFrom(new InternetAddress(EMAIL_TO));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(
EMAIL_USER_ADDRESS));
message.setSubject(EMAIL_SUBJECT);
message.setText(EMAIL_TEXT);
// use greenmail to store the message
user.deliver(message);
// fetch the e-mail from pop3 using javax.mail ..
Properties props = new Properties();
props.setProperty("mail.pop3.connectiontimeout", "5000");
Session session = Session.getInstance(props);
URLName urlName = new URLName("pop3", LOCALHOST,
ServerSetupTest.POP3.getPort(), null, user.getLogin(),
user.getPassword());
Store store = session.getStore(urlName);
store.connect();
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message[] messages = folder.getMessages();
assertNotNull(messages);
assertThat(1, equalTo(messages.length));
assertEquals(EMAIL_SUBJECT, messages[0].getSubject());
assertTrue(String.valueOf(messages[0].getContent())
.contains(EMAIL_TEXT));
assertEquals(EMAIL_TO, messages[0].getFrom()[0].toString());
}
}
POP3S
We’re changing the server setup here and use a dummy ssl certificate provider bundled with GreenMail – and another URLName ..
@Before
public void setUp() {
Security.setProperty("ssl.SocketFactory.provider",
DummySSLSocketFactory.class.getName());
mailServer = new GreenMail(ServerSetupTest.POP3S);
mailServer.start();
}
[..]
URLName urlName = new URLName("pop3s", LOCALHOST,
ServerSetupTest.POP3S.getPort(), null, user.getLogin(),
user.getPassword());
[..]
SMTP
In the following test we’re setting up an SMTP server, we’re using javax.mail horror to send an e-mail and afterwards we’re validating it using some helpful functions from the GreenMail framework…
package it;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.icegreen.greenmail.user.UserException;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetupTest;
import com.sun.mail.smtp.SMTPTransport;
public class SmtpIT {
private static final String USER_PASSWORD = "abcdef123";
private static final String USER_NAME = "hascode";
private static final String EMAIL_USER_ADDRESS = "hascode@localhost";
private static final String EMAIL_TO = "someone@localhost.com";
private static final String EMAIL_SUBJECT = "Test E-Mail";
private static final String EMAIL_TEXT = "This is a test e-mail.";
private static final String LOCALHOST = "127.0.0.1";
private GreenMail mailServer;
@Before
public void setUp() {
mailServer = new GreenMail(ServerSetupTest.SMTP);
mailServer.start();
}
@After
public void tearDown() {
mailServer.stop();
}
@Test
public void getMails() throws IOException, MessagingException,
UserException, InterruptedException {
// setup user on the mail server
mailServer.setUser(EMAIL_USER_ADDRESS, USER_NAME, USER_PASSWORD);
// create the javax.mail stack with session, message and transport ..
Properties props = System.getProperties();
props.put("mail.smtp.host", LOCALHOST);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", ServerSetupTest.SMTP.getPort());
Session session = Session.getInstance(props, null);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(EMAIL_TO));
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(EMAIL_USER_ADDRESS, false));
msg.setSubject(EMAIL_SUBJECT);
msg.setText(EMAIL_TEXT);
msg.setSentDate(new Date());
SMTPTransport t = (SMTPTransport) session.getTransport("smtp");
t.connect(LOCALHOST, EMAIL_USER_ADDRESS, USER_PASSWORD);
t.sendMessage(msg, msg.getAllRecipients());
assertEquals("250 OK\n", t.getLastServerResponse());
t.close();
// fetch messages from server
MimeMessage[] messages = mailServer.getReceivedMessages();
assertNotNull(messages);
assertEquals(1, messages.length);
MimeMessage m = messages[0];
assertEquals(EMAIL_SUBJECT, m.getSubject());
assertTrue(String.valueOf(m.getContent()).contains(EMAIL_TEXT));
assertEquals(EMAIL_TO, m.getFrom()[0].toString());
}
}
SMTPS
For SMTPS we’re changing our setup a bit and we’re using the DummySSLSocketFactory shipped with GreenMail to avoid certificate errors here: Security.setProperty(“ssl.SocketFactory.provider”, DummySSLSocketFactory.class.getName());
package it;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.security.Security;
import java.util.Date;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.icegreen.greenmail.user.UserException;
import com.icegreen.greenmail.util.DummySSLSocketFactory;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetupTest;
import com.sun.mail.smtp.SMTPTransport;
public class SmtpsIT {
private static final String USER_PASSWORD = "abcdef123";
private static final String USER_NAME = "hascode";
private static final String EMAIL_USER_ADDRESS = "hascode@localhost";
private static final String EMAIL_TO = "someone@localhost.com";
private static final String EMAIL_SUBJECT = "Test E-Mail";
private static final String EMAIL_TEXT = "This is a test e-mail.";
private static final String LOCALHOST = "127.0.0.1";
private GreenMail mailServer;
@Before
public void setUp() {
Security.setProperty("ssl.SocketFactory.provider",
DummySSLSocketFactory.class.getName());
mailServer = new GreenMail(ServerSetupTest.SMTPS);
mailServer.start();
}
@After
public void tearDown() {
mailServer.stop();
}
@Test
public void getMails() throws IOException, MessagingException,
UserException, InterruptedException {
// setup user on the mail server
mailServer.setUser(EMAIL_USER_ADDRESS, USER_NAME, USER_PASSWORD);
// create the javax.mail stack with session, message and transport ..
Properties props = System.getProperties();
props.put("mail.smtps.host", LOCALHOST);
props.put("mail.smtps.auth", "true");
props.put("mail.smtps.port", ServerSetupTest.SMTPS.getPort());
Session session = Session.getInstance(props, null);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(EMAIL_TO));
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(EMAIL_USER_ADDRESS, false));
msg.setSubject(EMAIL_SUBJECT);
msg.setText(EMAIL_TEXT);
msg.setSentDate(new Date());
SMTPTransport t = (SMTPTransport) session.getTransport("smtps");
t.connect(LOCALHOST, EMAIL_USER_ADDRESS, USER_PASSWORD);
t.sendMessage(msg, msg.getAllRecipients());
assertEquals("250 OK\n", t.getLastServerResponse());
t.close();
// fetch messages from server
MimeMessage[] messages = mailServer.getReceivedMessages();
assertNotNull(messages);
assertEquals(1, messages.length);
MimeMessage m = messages[0];
assertEquals(EMAIL_SUBJECT, m.getSubject());
assertTrue(String.valueOf(m.getContent()).contains(EMAIL_TEXT));
assertEquals(EMAIL_TO, m.getFrom()[0].toString());
}
}
IMAP
Again we’re creating a user account first, we’re creating an e-mail message and afterwards pull the message from the IMAP server using the javax.mail API..
package it;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.user.UserException;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetupTest;
public class ImapIT {
private static final String USER_PASSWORD = "abcdef123";
private static final String USER_NAME = "hascode";
private static final String EMAIL_USER_ADDRESS = "hascode@localhost";
private static final String EMAIL_TO = "someone@localhost.com";
private static final String EMAIL_SUBJECT = "Test E-Mail";
private static final String EMAIL_TEXT = "This is a test e-mail.";
private static final String LOCALHOST = "127.0.0.1";
private GreenMail mailServer;
@Before
public void setUp() {
mailServer = new GreenMail(ServerSetupTest.IMAP);
mailServer.start();
}
@After
public void tearDown() {
mailServer.stop();
}
@Test
public void getMails() throws IOException, MessagingException,
UserException, InterruptedException {
// create user on mail server
GreenMailUser user = mailServer.setUser(EMAIL_USER_ADDRESS, USER_NAME,
USER_PASSWORD);
// create an e-mail message using javax.mail ..
MimeMessage message = new MimeMessage((Session) null);
message.setFrom(new InternetAddress(EMAIL_TO));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(
EMAIL_USER_ADDRESS));
message.setSubject(EMAIL_SUBJECT);
message.setText(EMAIL_TEXT);
// use greenmail to store the message
user.deliver(message);
// fetch the e-mail via imap using javax.mail ..
Properties props = new Properties();
Session session = Session.getInstance(props);
URLName urlName = new URLName("imap", LOCALHOST,
ServerSetupTest.IMAP.getPort(), null, user.getLogin(),
user.getPassword());
Store store = session.getStore(urlName);
store.connect();
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message[] messages = folder.getMessages();
assertNotNull(messages);
assertThat(1, equalTo(messages.length));
assertEquals(EMAIL_SUBJECT, messages[0].getSubject());
assertTrue(String.valueOf(messages[0].getContent())
.contains(EMAIL_TEXT));
assertEquals(EMAIL_TO, messages[0].getFrom()[0].toString());
}
}
IMAPS
Again we just need to change the server setup and the URLName …
@Before
public void setUp() {
Security.setProperty("ssl.SocketFactory.provider",
DummySSLSocketFactory.class.getName());
mailServer = new GreenMail(ServerSetupTest.IMAPS);
mailServer.start();
}
[..]
URLName urlName = new URLName("imaps", LOCALHOST,
ServerSetupTest.IMAPS.getPort(), null, user.getLogin(),
user.getPassword());
[..]
GreenMail Utilities
There are a lot of helper methods and utilities encapsuled in the GreenMailUtil class .. whatever you need from generating a random string with a specific length to extracting headers from an email part or creating a MimeMessage body from a string you should take a look at this class and its static methods.
GreenMailUtil’s JavaDocs can be found at here.
Tutorial Sources
Please feel free to to view and download the complete sources from this tutorial from my GitHub repository – or clone it with
git clone https://github.com/hascode/greenmail-it-samples
Troubleshooting
-
“javax.mail.AuthenticationFailedException: 535 5.7.8 Authentication credentials invalid at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:965)
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780)
at javax.mail.Service.connect(Service.java:366)
at javax.mail.Service.connect(Service.java:246)
at com.vw.resourcemanagementtool.service.SmtpIT.getMails(SmtpIT.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)[..]” This error occurs when using newer versions of GreenMail .. we need to provide more security information .. so simply replace the javax.mail.Session from the examples above with the following code:Session session = Session.getInstance(props, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(USER_NAME, USER_PASSWORD); } });
Article Updates
-
2019-01-11: Troubleshooting section with fix for authentication problem in newer GreenMail versions added.