GreenMail testing
GreenMail is open source library we can use to test our mail sending and receiving code.
For sending purposes GreenMail responds like a regular SMTP server but does not deliver any email. Messages can be extracted, verified and modified in our test cases.
Spring integration
To use GreenMail in Springs integration tests we can register GreenMail extension if we need to verify that correct mail is being sent.
We can also use GreenMail with TestContainers.
GreenMail extension
We start by including GreenMail as dependency to our project:
<dependency> <groupId>com.icegreen</groupId> <artifactId>greenmail-junit5</artifactId> <version>2.0.1</version> <scope>test</scope> </dependency>
and defining appropriate
test spring.mail.*
application.properties
spring.mail.host=localhost # greenmail default protocol port + 3000 as offset spring.mail.port=3025 spring.mail.username=user spring.mail.password=pass spring.mail.protocol=smtp # avoid setting this to true when using a per-test-method GreenMail server spring.mail.test-connection=false
We can then use GreenMail extension in our integration tests
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class MailControllerIT { @RegisterExtension static GreenMailExtension greenMail = new GreenMailExtension(ServerSetupTest.SMTP) .withConfiguration(GreenMailConfiguration .aConfig() // same as in test application.properties .withUser("user", "pass")) .withPerMethodLifecycle(false); @Autowired private TestRestTemplate testRestTemplate; @Test void shouldSendEmailWithCorrectPayloadToUser() { String payload = """ { "to": "example@mail.com", "subject": "text mail", "text": "example of plain text mail" } """; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> request = new HttpEntity<>(payload, headers); ResponseEntity<Void> response = this.testRestTemplate.postForEntity("/api/v1/mail/text", request, Void.class); assertEquals(200, response.getStatusCode().value()); await() .atMost(2, SECONDS) .untilAsserted( () -> { MimeMessage[] receivedMessages = greenMail.getReceivedMessages(); assertEquals(1, receivedMessages.length); MimeMessage receivedMessage = receivedMessages[0]; assertEquals(1, receivedMessage.getAllRecipients().length); assertEquals("example@mail.com", receivedMessage.getAllRecipients()[0].toString()); assertEquals("example of plain text mail", GreenMailUtil.getBody(receivedMessage)); assertEquals("text mail", receivedMessage.getSubject()); }); } }
GreenMail with Testcontainers
Testcontainers is library for providing throwaway, lightweight instances of anything that can run in a Docker container.
With Testcontainers we don’t need to mock our test environments. Instead, we define our test dependencies as code, then simply run your tests and containers will be created and then deleted.
Spring provides Testcontainers integrations with:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-testcontainers</artifactId> <scope>test</scope> </dependency>
Currently, there’s no specific Testcontainers module for GreenMail, but we can use
to verify that our email service indeed send email.GenericContainer
@SpringBootTest
@Testcontainers
public class MailServiceIT {
@Autowired
MailService mailService;
static final String MAIL_USERNAME = "username";
static final String MAIL_PASSWORD = "user_password";
@Container
static GenericContainer<?> greenMailContainer = new GenericContainer(DockerImageName.parse("greenmail/standalone:2.1.2"))
.waitingFor(Wait.forLogMessage(".*Starting GreenMail standalone.*", 1))
.withEnv("GREENMAIL_OPTS", "-Dgreenmail.setup.test.smtp -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.users=" + MAIL_USERNAME + ":" + MAIL_PASSWORD)
.withExposedPorts(ServerSetupTest.SMTP.getPort());
@DynamicPropertySource
static void configureMailHost(DynamicPropertyRegistry registry) {
registry.add("spring.mail.host", greenMailContainer::getHost);
registry.add("spring.mail.port", greenMailContainer::getFirstMappedPort);
registry.add("spring.mail.username", () -> MAIL_USERNAME);
registry.add("spring.mail.password", () -> MAIL_PASSWORD);
}
@Test
void sendingMailWithTextMessageWorks() throws MessagingException, IOException {
mailService.sendTextMessage("user@mail.com", "text message test", "plain text in mail body");
}
}
Since Testcontainers modules start on random ports to enable parallel testing and avoid port clashing, we need to wait for GreenMail Docker container startup by listening for appropriate log message and after startup dynamically register
properties by using Springs spring.mail.*
.DynamicPropertyRegistry
After GreenMail Testcontainers Spring setup, we can verify that our mail service is indeed sending mails.