Search code examples
javamockitojakarta-mailmime

Proper way to mock MIMEMessage?


I want to actually verify the fields set on the message, like the body, subject, from, etc… I’ve tried doing an argument captor as well which didn’t work and when I debug the code, the message is always showing null fields when it gets the javaMailSender.send(message) line.

Are there best practices in mocking MIMEMessage?

//EmailService.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

@Slf4j
public class EmailService {
    private JavaMailSender javaMailSender;

    @Autowired
    public EmailService(JavaMailSender javaMailSender) {
        this.javaMailSender = javaMailSender;
    }


    public void sendEmail() {
        MimeMessage message = javaMailSender.createMimeMessage();
        String body = "<html>\n" +
            "<p> This the body </p>\n" +
            "<br>\n";
        try {
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
            mimeMessageHelper.setSubject("subject");
            mimeMessageHelper.setTo("[email protected]");
            mimeMessageHelper.setFrom("[email protected]");
            mimeMessageHelper.setText(body, true);

            javaMailSender.send(message);

        } catch (MessagingException e) {
            log.error("error");
        }`enter code here`
    }
}

Below is the test

//EmailServiceTest.java

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.springframework.mail.javamail.JavaMailSender;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;


public class EmailServiceTest {
    private EmailService emailService;

    @Mock
    private JavaMailSender javaMailSender;

    @Mock
    private MimeMessage message;

    @Before
    public void setUp() throws IOException {
        initMocks(this);

        emailService = new EmailService(javaMailSender);
    }

    @Test
    public void sendEmail_sendsEmailWithCorrectContent() throws MessagingException {
        ArgumentCaptor<MimeMessage> mimeMessageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class);
        when(javaMailSender.createMimeMessage()).thenReturn(message);

        doNothing().when(javaMailSender).send(message);

        emailService.sendEmail();

        verify(javaMailSender, times(1)).send((mimeMessageArgumentCaptor.capture()));
        assertEquals("subject", mimeMessageArgumentCaptor.getValue().getSubject());
    }

}

Solution

  • It's generally easier to do this kind of testing using an integration test. Emails are often built using a template library such as freemarker, so an integration test is valuable in being able to verify that your template is correct as well.

    You could do unit testing by using PowerMock to create a mock MimeMessageHelper, but PowerMock can sometimes be slow at keeping up with changes to other testing libraries, and such tests often end up brittle and of little value. For example, you could verify that setSubject() was called on mimeMessageHelper, but does that produce the outcome that you really want to test (that the subject is set on message)?