1

The solutions in the question here use PowerMock. Is it possible to do this in just Mockito/JUnit?

I have a MailSender class with a method,

sendMail(String fromAddress, String message, String toAddress);

Internally, of course, it fills in the fields to construct a MimeMessage and uses the company's mail server to send the message.

But the last line is

javax.mail.Transport.send(mimeMessage);

I am trying to write a Junit test for the class but I do not know how to mock the Transport so that an email is not actually sent.

In MailSender, how should I modify the class to mock the call to javax.mail.Transport?

public class MailSender {


    static boolean sendMail(String fromAddress, String message, String toAddress) {
       //...
       MimeMessage message;
       //... create the message and fill in the fields
       // 
       transport.send(message);

    }
}

For the JUnit test, I do not know what to put here. I tried this - it does not compile but I wanted to know if I am on the right track.

class MailSenderTest {

@Mock
MailSender mailSender;

@InjectMocks
Transport transport

@Test
void sendMail() {
    Mockito.when(transport.sendMessage(any(), any()).thenReturn(true);
    assertEquals(true, mailSender.sendMail("me@gmail.com", "hi", ""you@gmail.com"");
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
likejudo
  • 3,396
  • 6
  • 52
  • 107

1 Answers1

2

Modern versions of Mockito allow you to mock static methods.

First, you need to include the inline mock-maker. Just replace your mockito-core artifact with the mockito-inline artifact. E.g., if you're using Maven:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.10.0</version>
    <scope>test</scope>
</dependency>

Then, you can crate a MockedStatic for the Transport class, call your sendMail method and verify that it passes the right arguments to Transport.send. E.g., as a simplistic example, I've only verified the fromAddress is used correctly:

@Test
public void testSendMail() throws Exception {
    try (MockedStatic<Transport> mockedTransport = Mockito.mockStatic(Transport.class)) {
        MailSender.sendMail("from", "message", "to");
        mockedTransport.verify(() -> Transport.send(Mockito.argThat(message -> {
            try {
                // This just verified the "from" of the message
                // but of course you can have more elaborate logic here
                return message.getFrom()[0].toString().contains("from");
            } catch (MessagingException e) {
                return false;
            }
        })));
    }
}
likejudo
  • 3,396
  • 6
  • 52
  • 107
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • thank you! I will try it out. Should I use Spy also? – likejudo Dec 17 '22 at 22:40
  • @likejudo use `spy` on what? Here you're mocking the behavior of `Transport`, and then verifying that given some arguments, `MailSender` calls `Transport.send` with the message object you expected. I don't see what you'd need to spy here - could you clarify what you meant please? – Mureinik Dec 17 '22 at 22:42
  • The `MailSender` class has a `@Value` field. (SpringBoot app) – likejudo Dec 18 '22 at 00:21
  • @Murenik I got an error: `org.mockito.exceptions.base.MockitoException: An unexpected error occurred while verifying a static stub To correctly verify a stub, invoke a single static method of javax.mail.Transport in the provided lambda. For example, if a method 'sample' was defined, provide a lambda or anonymous class containing the code () -> Transport.sample() or Transport::sample` – likejudo Dec 18 '22 at 03:02
  • @likejudo that shouldn't happen with this sample. Can you share your code? – Mureinik Dec 18 '22 at 08:45
  • It works now. Thank you for your help which came at a critical time for me. Shalom and God bless you – likejudo Dec 19 '22 at 19:00