How to test JavaMailSender?

2019-09-17 14:59发布

问题:

I have little problem. I created MailService to send mails. When I run program, it works. All properties to email I have in resources/application.properties. I'm using spring-boot-starter-mail.

@Service
public class MailService {
    private JavaMailSender javaMailSender;

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

    public void sendMail(String subject, String messageContent, String recipient)
            throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
        messageHelper.setTo(recipient);
        messageHelper.setSubject(subject);
        messageHelper.setText(messageContent);
        javaMailSender.send(mimeMessage);
    }
}

But I don't have idea how can I create test for it. I tried something like this, where I use org.jvnet.mock-javamail:mock-javamail, but it doesn't work:

public class MailServiceTest {
    private MailService mailService;

    @Mock
    private JavaMailSender javaMailSender;


    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mailService = new MailService(javaMailSender);
        Mailbox.clearAll();
    }

    @Test
    public void sendMailTest() throws MessagingException, IOException {
        String subject = "Some subject";
        String body = "Some contents.";
        String recipient = "test@test.com";

        mailService.sendMail(subject, body, recipient);
        List<Message> inbox = Mailbox.get(recipient);
        assertTrue(inbox.size() == 1);
        assertEquals(subject, inbox.get(0).getSubject());
        assertEquals(body, inbox.get(0).getContent());
    }
}

回答1:

I think you are getting unit test / mocking wrong. It seems that you want to create a unit test; but expect the results of a integration test.

What you can do with a unit test here:

  • provide a mocked JavaMailSender (as you already)
  • use verify on that mock later on

In other words: you are mocking the actual sending of a mail. Thus you can't expect that a mail will show up somewhere!

The only thing possible: ensure that the method calls you expect to see actually take place. But that basically leads you to write a test case that simply "re-implements" your production code using verify calls. That isn't too helpful.

Probably you should rather look into a real integration test here. Send a real email; and check a real inbox if that mail shows up there.



回答2:

You can test how MimeMessage was formed with:

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 = "example@example.com"
        EmailRequest request = new EmailRequest();
        request.setRecipient(recipient);
        emailServiceImpl.send(request);
        assertEquals(recipient, mimeMessage.getRecipients(RecipientType.TO)[0].toString());
    }
}


回答3:

I use Mockito to mock a JavaMailSender bean and instantiate it in a class annotated with @Configuration that runs only using a test profile:

import static org.mockito.Mockito.*;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import org.springframework.context.annotation.*;
import org.springframework.mail.javamail.JavaMailSender;

@Profile("test")
@Configuration
public class EmailServiceTest {
    @Bean
    @Primary
    public JavaMailSender javaMailSender() {
        JavaMailSender javaMailSender = mock(JavaMailSender.class);
        when(javaMailSender.createMimeMessage()).thenReturn(new MimeMessage((Session) null));
        return javaMailSender;
    }
}