TestNG Email Testing Guide
Master email testing in Java applications using TestNG and Mail7. This comprehensive guide shows you how to implement robust email testing with parallel execution, data providers, and advanced TestNG features.
Setting Up TestNG Email Testing
Add the following dependencies to your Maven project:
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mail7</groupId>
<artifactId>mail7-java-sdk</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Basic Test Structure
import org.testng.annotations.*;
import com.mail7.sdk.Mail7Client;
public class EmailTest {
private Mail7Client mail7Client;
private EmailService emailService;
@BeforeClass
public void setUp() {
mail7Client = new Mail7Client(System.getenv("MAIL7_API_KEY"));
emailService = new EmailService();
}
@Test
public void testEmailDelivery() {
String testEmail = mail7Client.generateEmail();
emailService.sendEmail(testEmail, "Test Subject", "Test Content");
List emails = mail7Client.getEmails(testEmail);
Assert.assertFalse(emails.isEmpty());
Assert.assertEquals(emails.get(0).getSubject(), "Test Subject");
}
@AfterClass
public void tearDown() {
mail7Client.cleanup();
}
}
Advanced TestNG Features
1. Parallel Test Execution
@Test(threadPoolSize = 3, invocationCount = 10)
public void testParallelEmailSending() {
String testEmail = mail7Client.generateEmail();
emailService.sendEmail(testEmail, "Parallel Test", "Content");
List emails = mail7Client.getEmails(testEmail);
Assert.assertFalse(emails.isEmpty());
}
2. Data Providers
@DataProvider(name = "emailData")
public Object[][] emailData() {
return new Object[][] {
{"Welcome", "Welcome to Mail7"},
{"Password Reset", "Reset your password"},
{"Verification", "Verify your account"}
};
}
@Test(dataProvider = "emailData")
public void testMultipleEmails(String subject, String content) {
String testEmail = mail7Client.generateEmail();
emailService.sendEmail(testEmail, subject, content);
List emails = mail7Client.getEmails(testEmail);
Assert.assertEquals(emails.get(0).getSubject(), subject);
Assert.assertTrue(emails.get(0).getContent().contains(content));
}
@DataProvider(name = "emailTemplates")
public Object[][] provideEmailTemplates() {
return new Object[][] {
{"welcome", Map.of("username", "John", "company", "Acme Inc")},
{"password-reset", Map.of("resetLink", "https://example.com/reset")},
{"verification", Map.of("code", "123456", "expiry", "24 hours")}
};
}
@Test(dataProvider = "emailTemplates")
public void testMultipleTemplates(String templateName, Map data) {
String testEmail = mail7Client.generateEmail();
emailService.sendTemplateEmail(testEmail, templateName, data);
List emails = mail7Client.getEmails(testEmail);
Assert.assertFalse(emails.isEmpty());
Email email = emails.get(0);
// Verify template-specific content
for (Map.Entry entry : data.entrySet()) {
Assert.assertTrue(email.getBody().contains(entry.getValue()));
}
}
3. Groups and Dependencies
@Test(groups = "setup")
public void testEmailSetup() {
// Setup code
}
@Test(groups = "send", dependsOnGroups = "setup")
public void testEmailSending() {
// Email sending test
}
@Test(groups = "cleanup", dependsOnGroups = "send", alwaysRun = true)
public void cleanupTest() {
// Cleanup code
}
@Test(groups = "setup")
public void testEmailAccountCreation() {
String testEmail = mail7Client.generateEmail();
boolean created = emailService.createAccount(testEmail);
Assert.assertTrue(created);
}
@Test(groups = "critical", dependsOnGroups = "setup")
public void testPasswordReset() {
String testEmail = mail7Client.generateEmail();
emailService.initiatePasswordReset(testEmail);
List emails = mail7Client.getEmails(testEmail);
Assert.assertFalse(emails.isEmpty());
Assert.assertTrue(emails.get(0).getSubject().contains("Password Reset"));
}
Testing Scenarios
1. Email Template Testing
@Test
public void testEmailTemplate() {
String testEmail = mail7Client.generateEmail();
Map templateData = new HashMap<>();
templateData.put("name", "John");
templateData.put("action", "verify");
emailService.sendTemplate(testEmail, "verification", templateData);
List emails = mail7Client.getEmails(testEmail);
String content = emails.get(0).getHtmlContent();
Assert.assertTrue(content.contains("John"));
Assert.assertTrue(content.contains("verify"));
}
2. Retry Logic
@Test(retryAnalyzer = EmailRetryAnalyzer.class)
public void testWithRetry() {
String testEmail = mail7Client.generateEmail();
emailService.sendEmail(testEmail, "Retry Test", "Content");
// Test logic with potential for transient failures
}
public class EmailRetryAnalyzer implements IRetryAnalyzer {
private int count = 0;
private static final int MAX_RETRY = 3;
@Override
public boolean retry(ITestResult result) {
if (!result.isSuccess()) {
if (count < MAX_RETRY) {
count++;
return true;
}
}
return false;
}
}
public class RetryAnalyzer implements IRetryAnalyzer {
private int count = 0;
private static final int MAX_RETRY = 3;
@Override
public boolean retry(ITestResult result) {
if (!result.isSuccess()) {
if (count < MAX_RETRY) {
count++;
return true;
}
}
return false;
}
}
@Test(retryAnalyzer = RetryAnalyzer.class)
public void testEmailDeliveryWithRetry() {
String testEmail = mail7Client.generateEmail();
emailService.sendEmailWithRetry(testEmail, "Test Subject", "Content");
await().atMost(30, TimeUnit.SECONDS)
.pollInterval(2, TimeUnit.SECONDS)
.until(() -> !mail7Client.getEmails(testEmail).isEmpty());
}
3. Groups and Dependencies
@Test(groups = "setup")
public void testEmailAccountCreation() {
String testEmail = mail7Client.generateEmail();
boolean created = emailService.createAccount(testEmail);
Assert.assertTrue(created);
}
@Test(groups = "critical", dependsOnGroups = "setup")
public void testPasswordReset() {
String testEmail = mail7Client.generateEmail();
emailService.initiatePasswordReset(testEmail);
List emails = mail7Client.getEmails(testEmail);
Assert.assertFalse(emails.isEmpty());
Assert.assertTrue(emails.get(0).getSubject().contains("Password Reset"));
}
Advanced Testing Patterns
1. Parallel Test Execution with Thread Safety
public class ThreadSafeEmailTest {
private static final ThreadLocal mail7Client = new ThreadLocal<>();
private static final ThreadLocal testEmail = new ThreadLocal<>();
@BeforeMethod
public void setUp() {
mail7Client.set(new Mail7Client(System.getenv("MAIL7_API_KEY")));
testEmail.set(mail7Client.get().generateEmail());
}
@Test(threadPoolSize = 5, invocationCount = 20)
public void testConcurrentEmailSending() {
EmailService emailService = new EmailService();
String currentEmail = testEmail.get();
emailService.sendEmail(currentEmail,
"Concurrent Test " + Thread.currentThread().getId(),
"Content");
List emails = mail7Client.get().getEmails(currentEmail);
Assert.assertFalse(emails.isEmpty());
}
@AfterMethod
public void tearDown() {
mail7Client.get().cleanup();
mail7Client.remove();
testEmail.remove();
}
}
2. Retry Logic for Flaky Tests
public class RetryAnalyzer implements IRetryAnalyzer {
private int count = 0;
private static final int MAX_RETRY = 3;
@Override
public boolean retry(ITestResult result) {
if (!result.isSuccess()) {
if (count < MAX_RETRY) {
count++;
return true;
}
}
return false;
}
}
@Test(retryAnalyzer = RetryAnalyzer.class)
public void testEmailDeliveryWithRetry() {
String testEmail = mail7Client.generateEmail();
emailService.sendEmailWithRetry(testEmail, "Test Subject", "Content");
await().atMost(30, TimeUnit.SECONDS)
.pollInterval(2, TimeUnit.SECONDS)
.until(() -> !mail7Client.getEmails(testEmail).isEmpty());
}
3. Custom Listeners for Email Testing
public class EmailTestListener implements ITestListener {
private static final Logger logger = LoggerFactory.getLogger(EmailTestListener.class);
@Override
public void onTestStart(ITestResult result) {
logger.info("Starting email test: {}", result.getName());
}
@Override
public void onTestSuccess(ITestResult result) {
logger.info("Email test passed: {}", result.getName());
}
@Override
public void onTestFailure(ITestResult result) {
logger.error("Email test failed: {}", result.getName());
// Capture email server state for debugging
captureEmailServerState(result);
}
private void captureEmailServerState(ITestResult result) {
Object instance = result.getInstance();
if (instance instanceof EmailTest) {
EmailTest test = (EmailTest) instance;
test.dumpEmailLogs();
}
}
}
Advanced Assertions and Verifications
public class EmailAssertions {
@Test
public void testComplexEmailValidation() {
String testEmail = mail7Client.generateEmail();
EmailTemplate template = new EmailTemplate()
.withSubject("Welcome to {{company}}")
.withBody("Dear {{name}},\nWelcome to {{company}}!")
.withData(Map.of(
"name", "John Doe",
"company", "Acme Inc"
));
emailService.sendTemplateEmail(testEmail, template);
List emails = mail7Client.getEmails(testEmail);
Email received = emails.get(0);
// Soft assertions to collect all failures
SoftAssert softAssert = new SoftAssert();
softAssert.assertEquals(received.getSubject(),
"Welcome to Acme Inc", "Subject template not properly rendered");
softAssert.assertTrue(received.getBody().contains("Dear John Doe"),
"Name not properly rendered in body");
softAssert.assertTrue(received.getBody().contains("Welcome to Acme Inc"),
"Company not properly rendered in body");
softAssert.assertAll();
}
@Test
public void testEmailTimingAndMetrics() {
String testEmail = mail7Client.generateEmail();
long startTime = System.currentTimeMillis();
emailService.sendEmail(testEmail, "Performance Test", "Content");
List emails = mail7Client.getEmails(testEmail);
long deliveryTime = System.currentTimeMillis() - startTime;
Assert.assertFalse(emails.isEmpty(), "Email not delivered");
Assert.assertTrue(deliveryTime < 5000,
"Email delivery took too long: " + deliveryTime + "ms");
Email email = emails.get(0);
Assert.assertNotNull(email.getHeaders().get("X-Mail7-Delivery-Time"),
"Delivery time header missing");
}
}
Integration with TestNG XML
<!-- testng.xml -->
<suite name="Email Test Suite" parallel="methods" thread-count="5">
<listeners>
<listener class-name="com.example.EmailTestListener"/>
</listeners>
<test name="Template Tests">
<groups>
<run>
<include name="templates"/>
</run>
</groups>
<classes>
<class name="com.example.EmailTemplateTest"/>
</classes>
</test>
<test name="Performance Tests">
<groups>
<run>
<include name="performance"/>
</run>
</groups>
<classes>
<class name="com.example.EmailPerformanceTest"/>
</classes>
</test>
</suite>
Best Practices and Tips
1. Test Organization
- Group related tests using TestNG groups
- Use meaningful test names that describe the scenario
- Implement proper test isolation using @BeforeMethod and @AfterMethod
- Use parallel execution carefully, ensuring thread safety
2. Error Handling
- Implement retry logic for flaky network-dependent tests
- Use soft assertions when checking multiple conditions
- Add proper logging and monitoring for debugging
- Handle timeouts appropriately with explicit wait conditions
3. Performance Considerations
- Use thread pools appropriately for parallel execution
- Clean up test data after each test run
- Monitor and log performance metrics
- Implement proper timeout handling for long-running tests