I’ve been ignoring setting up a proper unit-test regarding email functionality for some time now mainly because most of the cases a quick and dirty test email to myself would do it. Well, quick’n’dirty hack no more and this is a proper way to test your Spring Email functionality using an easy to use library called GreenMail.
Let’s start with the POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>SpringEmailTest</groupId>
<artifactId>SpringEmailTest</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!--Spring Dependencies-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--Testing Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>${greenmail.version}</version>
</dependency>
<!--Email Dependencies -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>${javax-mail.version}</version>
</dependency>
</dependencies>
<properties>
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.2.2.RELEASE</spring.version>
<junit.version>4.10</junit.version>
<javax-mail.version>1.5.0</javax-mail.version>
<greenmail.version>1.3.1b</greenmail.version>
</properties>
</project>
The Spring Configuration looks like this:
package com.dimitrisli.springEmailTest.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import java.util.Properties;
@Configuration
@ImportResource(value = "classpath:/spring/appXMLContext.xml")
@PropertySource(value = "classpath:/properties/application.properties")
public class AppConfig {
@Bean
public JavaMailSenderImpl emailSender(@Value("${email.host}") String emailHost,
@Value("${email.port}") Integer emailPort,
@Value("${email.username}") String username,
@Value("${email.pass}") String password){
JavaMailSenderImpl emailSender = new JavaMailSenderImpl();
emailSender.setHost(emailHost);
emailSender.setPort(emailPort);
emailSender.setUsername(username);
emailSender.setPassword(password);
//emailSender.setDefaultEncoding("UTF_8");
Properties mailProps = new Properties();
mailProps.setProperty("mail.transport.protocol","smtp");
mailProps.setProperty("mail.smtp.auth","true");
mailProps.setProperty("mail.smtp.starttls.enable","true");
mailProps.setProperty("mail.debug","false");
emailSender.setJavaMailProperties(mailProps);
return emailSender;
}
}
A few things to note here:
– The @Configuration annotation marks the Spring Java Configuration context.
– The @PropertySource is retrieving the properties file from classpath. Apparently @PropertySource is not playing well with @Value injection therefore a workaround this is to include an XML context just to incude the property placeholder reference.
– The @ImportResource is the way of including an XML context file from the classpath.
– The beans are defined using the @Bean annotation.
– The @Value annotation is automatically injecting the properties values into the variables.
The referenced application.properties that is including the Email details (in this case setup to be Gmail) looks like this:
email.host=smtp.gmail.com
email.port=25
email.username=yourGmailAtgmailDotcom
email.pass=yourPass
As noted above to resolve an inconsistency between the @PropertySource and @Value we need to introduce an XML context that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--In Java @Configuration POJOs the @PropertySource is
not playing well with @Value. To resolve this we introduce
just this reference that is doing the trick. No other dependency on
XML configuration should be needed.-->
<context:property-placeholder />
</beans>
Finally the JUnit test looks like this:
package com.dimitrisli.springEmailTest;
import com.dimitrisli.springEmailTest.config.AppConfig;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.GreenMailUtil;
import com.icegreen.greenmail.util.ServerSetupTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import javax.mail.Message;
import javax.mail.MessagingException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class EmailTest {
@Resource
private JavaMailSenderImpl emailSender;
private GreenMail testSmtp;
@Before
public void testSmtpInit(){
testSmtp = new GreenMail(ServerSetupTest.SMTP);
testSmtp.start();
//don't forget to set the test port!
emailSender.setPort(3025);
emailSender.setHost("localhost");
}
@Test
public void testEmail() throws InterruptedException, MessagingException {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("[email protected]");
message.setTo("[email protected]");
message.setSubject("test subject");
message.setText("test message");
emailSender.send(message);
Message[] messages = testSmtp.getReceivedMessages();
assertEquals(1, messages.length);
assertEquals("test subject", messages[0].getSubject());
String body = GreenMailUtil.getBody(messages[0]).replaceAll("=\r?\n", "");
assertEquals("test message", body);
}
@After
public void cleanup(){
testSmtp.stop();
}
}
– JUnit is configured to run with Spring using the SpringJUnit4ClassRunner.class
– JUnit is loading the Spring context as instructed by the @ContextConfiguration annotation pointing to the Java Configuration class.
– The @Resource annotation is autowiring the bean dependency directly on our test conveniently having it initialised during the test runtime.
– We have an init() activity marked by JUnit’s @Before annotation where we instantiate our GreenMail mail server and changing the port of our email service to a test one.
– The test method itself is a simple manner of initialising a test SimpleMailMessage and sending it via our email service that gets intercepted by GreenMail and further queried upon concerning the data received.
– A cleanup functionality is closing the resourced during the @After JUnit instructed method.
The source code can be found in this Github repo.