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("test@sender.com"); message.setTo("test@receiver.com"); 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.