Spring Email Testing using GreenMail

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.

Spring JUnit How to Rollback Database Transaction

It’s common when you are testing your DAOs at a safe TDD environment to perform the DB action, assertThat what you’ve done is as expected and then safely rollback the transaction. All should look untouched at the DB level (except the DB sequences that are always moving forward) thereafter.

The annotations needed in the JUnit test class to achieve this are:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/pathTo/spring/context/applicationContext.xml")
@TransactionConfiguration(transactionManager = "jdbcTransactionManager", defaultRollback = true)
@Transactional
public class LocationDaoTest {
  @Inject daoInTest;
  @Test
  public void testDAOsCRUD(){}
}

Notes:

  • The ContextConfiguration annotation points to the main application’s context configuration file.
  • The TransactionConfiguration annotation points to your transaction manager defined in your application context which can either be a DataSourceTransactionManager for JDBC operations or a HibernateTransactionManager for Hibernate related ORM operations.
  • The defaultRollback=true attribute of the TransactionConfiguration is responsible for rolling back safely our test transactions.

Maven Integration Tests

Unit tests shouldn’t take long to complete and by definition should test a unit in isolation. Therefore unit tests shouldn’t include database or web framework resources.

Integration Testing is responsible putting everything together and check whether things work as expected. Maven has a special phase in its lifecycle called integration-tests that accommodates and executes integration related tests by calling mvn integration-test. A nice technique a colleague suggested is to have a name convention for integration tests and explicitly exclude/include during related phases in the Maven Surefire plugin. Assuming the naming convention referring to classes meant to be Integration Tests have the “IntegrationTest” suffix, the POM configuration of the maven-surefire-plugin would look something like this:

  <build>
  ...
   	<plugins>
   		<plugin>
   			<artifactId>maven-surefire-plugin</artifactId>
   			<executions>
   				<execution>
   					<id>unit-tests</id>
   					<phase>test</phase>
   					<goals>
   						<goal>test</goal>
   					</goals>
   					<configuration>
	   					<excludes>
	   						<exclude>**/*IntegrationTest.java</exclude>
	   					</excludes>
   					</configuration>
   				</execution>
   				<execution>
   					<id>integration-tests</id>
   					<phase>integration-test</phase>
   					<goals>
   						<goal>test</goal>
   					</goals>
   					<configuration>
   						<includes>
   							<<include>**/*IntegrationTest.java</include>
   						</includes>
   					</configuration>
   				</execution>
   			</executions>
   		</plugin>
   ...... 
    </plugins>
.....
   </build>

JUnit 4 Showcase – assertThat and Hamcrest Matchers

Surely assertEquals() can get you far but you will be amazed with the versatility of assertThat() combined with the expressive and human friendly Matchers of the Hamcrest library.

The needed static imports in your JUnit 4 test case:

import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;
import static org.hamcrest.CoreMatchers.*;

You could use the is() and not() matchers like so:

	@Test
	public void assertThat1(){
		double d = 1.3;
		assertThat(d, is(1.3));
		assertThat(d, is(not(1.4)));
	}

or equalTo():

	@Test
	public void assertThat2(){
		String str="test";
		assertThat(str, equalTo("test"));
		assertThat(str, not(equalTo("anotherTest")));
	}

or anyOf():

	@SuppressWarnings("unchecked")
	@Test
	public void assertThat3(){
		String str="test";
		assertThat(str, anyOf(is("test"), is("this"), is("that")));
		assertThat(str, not(anyOf(is("tasty"), is("this"), is("that"))));
	}

or lessThan() and hasItem() for Collections:

	@SuppressWarnings("serial")
	@Test
	public void assertThatCollections1(){
		List<String> list = new ArrayList<String>(){{add("one");add("two");add("three");}};
		assertThat(list, hasItem("two"));
		assertThat(list, not(hasItem("ttwo")));
	}
	
	@SuppressWarnings("serial")
	@Test
	public void assertThatCollections2(){
		List<Integer> list = new ArrayList<Integer>(){{add(1);add(2);add(3);}};
		assertThat(list, not(hasItem(lessThan(0))));
	}

or hasKey() and hasValue() particularly for Maps:

	@Test
	public void assertThatMaps1(){
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("one", 1);
		map.put("two", 2);
		map.put("three", 3);
		assertThat(map, Matchers.<String,Integer>hasKey("one"));
		assertThat(map, Matchers.<String,Integer>hasValue(2));
	}

A thing to note in the above example is the way we need to explicitly define the types on the key/value Map pair before summoning hasKey() or hasValue(), a bit weird why type inference didn’t help us there.

Finally we could even query properties of Java Beans:

@Test
	public void assertThatProperty1(){
		
		class Bean<T> {
			private T t;
			public void setT(T t){this.t = t;}
			public T getT(){return this.t;}
		}
		
		Bean<String> myBean = new Bean<String>();
		myBean.setT("hi");
		assertThat(myBean, hasProperty("t",is("hi")));
		assertThat(myBean, hasProperty("t",startsWith("h")));
	}

The code can be found in this Gitbub repository.

JUnit 4 Showcase – Parameterized Tests

A very interesting feature is the usage of parameterized tests to save us of the trouble of repetitive test code and automatically call test cases using predefined collection of test data.

Everything starts with the @RunWith(Parameterized.class) annotation that needs to be applied on the test class this time. A special public static method annotated as @Parameters signals the collection of test data. This set will be called by JUnit behind the scenes once per each data set calling the constructor for the creation of a new test class. The constructor should allocate this collection of test data into encapsulated members. These members would be used by the @Test annotated methods performing the test cases.

An example would look like this:

package com.dimitrisli.junitshowcase.parameterizedTest;

import java.util.Arrays;
import java.util.Collection;

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;



@RunWith(Parameterized.class)
public class ParameterizedTesting {

	@Parameters
	public static Collection<?> data(){
		return Arrays.asList(new Object[][]{
				{2,3,6},
				{3,4,12},
				{4,5,20},
				{5,6,30},
				{6,7,42}
		});
	}
	
	private int a;
	private int b;
	private int result;
	
	public ParameterizedTesting(int a, int b, int result){
		this.a = a;
		this.b = b;
		this.result = result;
	}
	
	@Test
	public void multiplicationCalculation(){
		int calcResult = a*b;
		System.out.println("a="+a+", b="+b+". Expected result= "+result+", and calculated result= "+calcResult);
		Assert.assertEquals(result, calcResult);
	}
	
}

that yields back the result:

a=2, b=3. Expected result= 6, and calculated result= 6
a=3, b=4. Expected result= 12, and calculated result= 12
a=4, b=5. Expected result= 20, and calculated result= 20
a=5, b=6. Expected result= 30, and calculated result= 30
a=6, b=7. Expected result= 42, and calculated result= 42

This code can be found in this Github repository.

JUnit 4 Showcase – testing exceptions

A nice way to determine we are expecting an exception during a test case is to use the @Test annotation with the expected parameter.

A trivial example would look like so:

package com.dimitrisli.junitshowcase.exceptionTesting;

import org.junit.Test;

public class ExceptionsTesting {

	@Test(expected = NullPointerException.class)
	public void method(){
		throw new NullPointerException();
	}

	@Test(expected = IllegalArgumentException.class)
	public void method2(){
		throw new IllegalArgumentException();
	}
}

This code extract along with the other examples of the JUnit 4 Showcase can be found in this Github repository.

JUnit 4 Showcase – Using timeouts

An interesting way to performance test the time it takes for an expensive operation to complete is to use JUnit 4’s timeouts. This way we can capture poor response times. In this case the test case fails if it takes longer than the predefined time in milliseconds to complete.

A trivial example would look like this:

package com.dimitrisli.junitshowcase.performanceTesting;

import org.junit.Test;

import junit.framework.Assert;

public class PerformanceTest {

	@Test(timeout=100)
	public void method(){
		Assert.assertTrue(true);
	}
}

This code extract along with the full JUnit 4 showcase can be found in this Github repository.

JUnit 4 Showcase – Setting up and optimising unit tests

JUnit 4 has been a major rewrite of the JUnit API mainly:

  • it uses annotations
  • a test can be any POJO class, no need to extend TestCase or have special name conventions for test classes and methods. Although the main rules remain that each JUnit method needs to be public void and have no arguments.

In case we want to run initialisation code prior and post each test case (JUnit method) we can use the @Before and @After annotations saving us from the trouble of initialising the same code in each test case.

Similarly, if we need to run initialisation code prior and post every test class this time we can use the @BeforeClass and @AfterClass annotation. Other than these annotations, these particular methods need to be static.

Having as an example the following code:

package com.dimitrisli.junitshowcase.beforeAfter;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;


public class BeforeAfterTest {

	@BeforeClass
	public static void initialiseClass(){
		System.out.println("init class");
	}
	
	@Before
	public void initialiseTest(){
		System.out.println("init test");
	}
	
	@Test
	public void test1(){
		System.out.println("inside test1");
		Assert.assertTrue(true);
	}

	@Test
	public void test2(){
		System.out.println("inside test2");
		Assert.assertTrue(true);
	}
	
	@After
	public void teardownTest(){
		System.out.println("teardown test");
	}
	
	@AfterClass
	public static void teardownClass(){
		System.out.println("teardown class");
	}
}

produces the output:

init class
init test
inside test1
teardown test
init test
inside test2
teardown test
teardown class

The code can be found in this Github repository.

Maven Surefire plugin patterns

Today I hit a case where some of my tests wouldn’t be picked up to run during Maven’s testing phase. The reason for that is the way the Surefire plugin is picking up names to include by default, namely the **/*Test.java, **/Test*.java, **/*TestCase.java name patterns.

If we want to include all the tests adhering to a common naming convention we can use regex to explicitly specify inclusion in the Surefire plugin:

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.11</version>
        <configuration>
          <includes>
            <include>%regex[.*MyNamingConvention*]</include>
          </includes>
        </configuration>
      </plugin>
    </plugins>
  </build>
  [...]
</project>