How to Unit Test Sending Emails Without Actual Delivery

Testing application before rolling them to clients is crucial for any platform. Many applications require generating email as part of their workflow. Imagine you need to test an application that sends emails to genuine customers with accurate data, but none of these customers should actually get these emails. This page gives you step-by-step instructions on how to accomplish this task.

Unit Testing

Almost every programming language has frameworks for Unit Testing. In fact, many integrated development environments (IDE) support unit testing without any third-party integration. Such IDEs include Microsoft Visual Studio, Eclipse, and others. Regardless of the programming language or IDE you use, the method to perform unit testing is a two-step process:

  1. Run tests by executing a self-contained unit of code
  2. Verify the code executed correctly
Step 1
For demonstration purposes, we will be using Java as the programming language and JUnit as the testing framework. Assume we're testing a class called ClientRegistration, which has a method called confirmEmail, which sends a confirmation email to the client with further instructions.

Skeleton Code In Java


import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ClientRegistrationTest {
	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}
	@Test
	public void testConfirmEmail() {
		new ClientRegistration().confirmEmail(); //Sends an email to an SMTP server.
		
		//TODO: Have to confirm if the email is sent correctly.
		//Refer to step 2 for this.
	}
}

The code you see above calls the method confirmEmail but does not check if the email is actually sent.

DevNullSmtp

You will need to download a small utility called DevNullSmtp, which contains a dummy SMTP server that accepts emails from the code that needs to be tested. DevNullSmtp is written in Java and therefore, you will need JRE to run it. We recommend using JRE 1.8.

Configuration SMTP Server For Your Application

Before testing begins, you need to configure the SMTP server in your application to this dummy SMTP Server. Assume in production, the hostname of your SMTP server is mail.yourcompany.com. There are two ways to change this value:

  • If the value of the SMTP server is in a configuration file, change it from mail.yourcompany.com to localhost.
  • If changing the hostname in not an option, you could temporarily modify the hosts file at your OS level and skip a DNS lookup. The location of hosts file on Windows is C:\Windows\System32\drivers\etc\hosts and on Linux it is /etc/hosts.

Now that all the prerequisites are discussed, let's talk about the next step.

Step 2

Once an email is sent (step 1), the next step is to verify the email was indeed received by the SMTP server and the body of the message is correct. This is where DevNullSmtp comes into picture, which is a dummy SMTP server with the ability to optionally save incoming emails to a folder on the disk.

There are two ways to spawn DevNullSmtp:

  1. Run it as a stand-alone application
  2. Spawn it within the setUp() method of JUnit and terminate it in tearDown() method.

Refer to the documentation for DevNullSmtp to see how to start DevNullSmtp as a stand-alone application.

The second method is more interesting because you can spawn DevNullSmtp when testing starts and terminates when done.

Skeleton Code In Java


import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ClientRegistrationTest {
	
	Process proc = null;
	@Before
	public void setUp() throws Exception {
		//Spawn a new process for DevNullSmtp on port 25 and save emails to /testEmails folder
		String[] cmdarray = {"java", "-jar", "DevNullSmtp.jar", "-console", "-s", "/testEmails"};
		proc = Runtime.getRuntime().exec(cmdarray);
		
		//Alternatively, call com.synametrics.devnull.DevNullSmtp.main() method
		//which will run DevNullSmtp in the same process as JUnit.
		
	}

	@After
	public void tearDown() throws Exception {
		if(proc != null) {
			proc.destroy(); //Terminate DevNullSmtp once testing is over.
		}
	}
	@Test
	public void testConfirmEmail() {
		new ClientRegistration().confirmEmail(); //Send an email, which will be saved in /testEmails folder
		 
		//Confirm a new *.eml file is saved in /testEmails folder.
		File folder = new File("/testEmails");
		assertEquals(1,  folder.listFiles().length);
	}
}


Analyzing setup()

The setUp() method in JUnit framework is used to start DevNullSmtp, which is configured to listen for incoming emails on TCP/IP port 25 and will save incoming emails to /testEmails. The spawned process is saved in the member variable proc.

Another way to start DevNullSmtp is to just call it's main method, provided the JAR file is in the CLASSPATH. However, this method is only available if my original test code is also in Java. You won't be able to call main if you're trying to perform unit testing in any other language, such as C#.

Analyzing tearDown()

Calling proc.destroy() terminates the spawned process, terminating DevNullSmtp.

Analyzing testConfirmEmail()

There are two sections in this method. The first line calls the code that needs to be tested, which is:
new ClientRegistration().confirmEmail();
Ideally, the above line should have generated an email that is received by the dummy SMTP Server: DevNullSmtp. The remainder of the function checks the newly saved file in /testEmails folder.

Summary

Using DevNullSmtp is an excellent way to test outbound emails. Messages sent to DevNullSmtp do not get delivered to any recipient. Instead, it can be used to perform unit testing on any application that sends emails.

Navigation

Social Media

Powered by 10MinutesWeb.com