How do I test the JavaMailSender of Spring
Asked Answered
D

6

21

I have a service that has injected the JavaMailSender. My service configures it and sends a mail. I'd like to intercept the raw mail to ensure the information is the correct. I'd like to do that in a JUnit.

How would you guys do that?

@Service
public class MyServiceImpl {

    @Autowired
    private JavaMailSender _mailSender;

    public void sendMail(String to, String body, String subject){
        ...
        _mailSender.something
        ...
    }
}
Decapitate answered 31/5, 2013 at 10:18 Comment(0)
S
26

I've done it using GreenMail. Take a look at my blog post about it where you'll also find a working example.

Sileas answered 31/5, 2013 at 10:23 Comment(1)
Website is down, it would be better if example code were added to the answer.Compulsion
D
5

You can use a test SMTP server, like Dumbster. See the example below:

@Test
    public void sendSimpleEmailWithCC() {
        // Runs a Dumbster simple SMTP server - default config
        SimpleSmtpServer server = SimpleSmtpServer.start();
        String from = "[email protected]";
        String to = "[email protected]";
        String messageText = "Good message";
        String title = "Test message";
        String cc = "[email protected]";
        Assert.assertTrue(mailSender
                .sendEmail(from, to, cc, title, messageText));
        server.stop();
        Assert.assertTrue(server.getReceivedEmailSize() == 1);
        Iterator emailIter = server.getReceivedEmail();
        SmtpMessage email = (SmtpMessage) emailIter.next();
        Assert.assertTrue(email.getHeaderValue("From").equals(from));
        Assert.assertTrue(email.getHeaderValue("To").equals(to));
        Assert.assertTrue(email.getHeaderValue("Cc").equals(cc));
        Assert.assertTrue(email.getHeaderValue("Subject")
                .equals("Test message"));
        Assert.assertTrue(email.getBody().equals(messageText));
    }
Denverdeny answered 31/5, 2013 at 11:12 Comment(2)
Do not use dumbster please. It's horribly broken: sourceforge.net/p/dumbster/bugs/15. Use greenmail instead.Dumbarton
The provided link was broken. @TrueDub, can you lease update it?Singlehandedly
S
3

If your goal is to use just Junit/Mockito and test how MimeMessage was formed before sending then the configuration below should be sufficient:

public class EmailServiceTest {

    private EmailServiceImpl emailServiceImpl;

    private JavaMailSender javaMailSender;

    private MimeMessage mimeMessage;

    @Before
    public void before() {
        mimeMessage = new MimeMessage((Session)null);
        javaMailSender = mock(JavaMailSender.class);
        when(javaMailSender.createMimeMessage()).thenReturn(mimeMessage);
        emailServiceImpl = new EmailService(javaMailSender);
    }

    @Test
    public void emailTest() {
        String recipient = "[email protected]"
        EmailRequest request = new EmailRequest();
        request.setRecipient(recipient);
        emailServiceImpl.send(request);
        assertEquals(recipient, mimeMessage.getRecipients(RecipientType.TO)[0].toString());
    }
}
Sharleensharlene answered 23/2, 2019 at 0:12 Comment(4)
(Session)null what is this?Lizbeth
@DenissM. It was almost a year since you asked which means this information might not relevant to you anymore. When a method is overloaded Java will pick the most specific method (in this case constructor). However, null without a type information could be any of the type. This is why you have to cast it to a specific type. https://mcmap.net/q/149856/-how-to-do-method-overloading-for-null-argumentMicroscopium
In this case you can also use Mockito.mock(MimeMessage.class) instead of new MimeMessage((Session) null). It's a bit more clear.Barr
About mocking MimeMessage: I tried this, but I got many NPEs, so It is better to use a real MimeMessage. Also it may be better to mock the absolute minimum.Goral
B
3

To add a more recent answer to this question and as the linked blog post from 2012 seems to be down occasionally, here's a full example of using GreenMail for writing integration tests with JUnit 5 (assuming you're using Spring Boot's auto-configuration for the JavaMailSender).

First, make sure to override the credentials and location of the mail server. You can add an application.yml file inside src/test/resources for this purpose:

spring:
  mail:
    password: springboot
    username: duke
    host: 127.0.0.1
    port: 3025 # default protocol port + 3000 as offset
    protocol: smtp
    test-connection: true

Next, Spring Boot's auto-configuration will configure the JavaMailSender to connect to GreenMail for your tests. You can use GreenMail's JUnit Jupiter extension to conveniently start/stop a GreenMail server for your tests:

<dependency>
  <groupId>com.icegreen</groupId>
  <artifactId>greenmail-junit5</artifactId>
  <version>1.6.1</version>
  <scope>test</scope>
</dependency>

... resulting in the following sample test:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class JavaMailSenderIT {

  @RegisterExtension
  static GreenMailExtension greenMail = new GreenMailExtension(ServerSetupTest.SMTP)
    .withConfiguration(GreenMailConfiguration.aConfig().withUser("duke", "springboot"))
    .withPerMethodLifecycle(false);

  @Autowired
  private JavaMailSender javaMailSender;

  @Test
  void shouldUseGreenMail() throws Exception {

    SimpleMailMessage mail = new SimpleMailMessage();
    mail.setFrom("[email protected]");
    mail.setSubject("A new message for you");
    mail.setText("Hello GreenMail!");
    mail.setTo("[email protected]");


    javaMailSender.send(mail);

    // awaitility
    await().atMost(2, SECONDS).untilAsserted(() -> {
      MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
      assertEquals(1, receivedMessages.length);

      MimeMessage receivedMessage = receivedMessages[0];
      assertEquals("Hello GreenMail!", GreenMailUtil.getBody(receivedMessage));
      assertEquals(1, receivedMessage.getAllRecipients().length);
      assertEquals("[email protected]", receivedMessage.getAllRecipients()[0].toString());
    });
  }
}

You can also use Testcontainers to start a local GreenMail container as your local sandbox email server.

Bobbobb answered 26/10, 2021 at 16:17 Comment(0)
P
0

Regarding test with GreenMail, you don't want any end to end encryption as it's in your local machine, and for that you need to add this properties to your application-test.properties file:

spring:
  mail:
    username: [email protected]
    password: *********
    host: 127.0.0.1
    port: 3025
    protocol: smtp
    test-connection: false
    properties:
      mail:
        smtp:
          starttls:
            required: false
            enable: false

Otherwise you'll get an error: STARTTLS is required but host does not support STARTTLS.

Pashalik answered 10/11, 2022 at 17:1 Comment(0)
U
0

Elaborating on @justinas-jakavonis answer I would suggesto to include this configuration bean in your src/test/java

@Configuration
public class JavaMailSenderTestConfiguration {

    private JavaMailSender javaMailSender;

    private MimeMessage mimeMessage;

    @Bean
    JavaMailSender mockMailSender() {
        mimeMessage = Mockito.mock(MimeMessage.class);
        javaMailSender = Mockito.mock(JavaMailSender.class);
        Mockito.when(javaMailSender.createMimeMessage()).thenReturn(mimeMessage);
        return javaMailSender;
    }
}
United answered 16/10, 2023 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.