TestNG Email Testing Guide

Updated February 14, 2024

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