仅用Mockito/JUnit如何Mock javax.mail.Transport类?
解决方案
一、仅用Mockito/JUnit实现:重构代码解耦依赖
Mockito默认不支持Mock静态方法,所以要不用PowerMock,必须先重构MailSender,把对Transport的静态调用改成可注入的依赖:
1. 重构MailSender类
把静态方法改为实例方法,封装邮件发送逻辑为可替换的接口:
import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; public class MailSender { // 定义接口封装发送逻辑 public interface MailTransport { void send(MimeMessage message) throws MessagingException; } private final MailTransport transport; // 生产环境使用默认实现 public MailSender() { this.transport = javax.mail.Transport::send; } // 测试环境注入Mock实现 public MailSender(MailTransport transport) { this.transport = transport; } // 改为实例方法 public boolean sendMail(String fromAddress, String content, String toAddress) { try { // 原有构造MimeMessage的逻辑示例 MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties())); mimeMessage.setFrom(new InternetAddress(fromAddress)); mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(toAddress)); mimeMessage.setText(content); transport.send(mimeMessage); return true; } catch (MessagingException e) { e.printStackTrace(); return false; } } }
2. 编写Mockito测试
现在可以正常Mock自定义的MailTransport接口,完全避免实际发邮件:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.any; @ExtendWith(MockitoExtension.class) class MailSenderTest { @Mock private MailSender.MailTransport mockTransport; @InjectMocks private MailSender mailSender; @Test void sendMail_shouldReturnTrueWhenSuccess() throws MessagingException { // Mock发送成功的场景 doNothing().when(mockTransport).send(any(MimeMessage.class)); boolean result = mailSender.sendMail("me@gmail.com", "hi", "you@gmail.com"); assertEquals(true, result); } @Test void sendMail_shouldReturnFalseWhenFailure() throws MessagingException { // Mock发送失败的场景 doThrow(new MessagingException("发送失败")).when(mockTransport).send(any(MimeMessage.class)); boolean result = mailSender.sendMail("me@gmail.com", "hi", "you@gmail.com"); assertEquals(false, result); } }
二、不重构代码的方案:使用PowerMock(不推荐)
如果必须保留原有静态方法和直接调用Transport.send的逻辑,才需要用PowerMock来Mock静态方法:
1. 添加PowerMock依赖(Maven示例)
<dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> </dependencies>
2. 编写PowerMock测试代码
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import javax.mail.Transport; import javax.mail.internet.MimeMessage; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; @RunWith(PowerMockRunner.class) @PrepareForTest(Transport.class) // 指定要Mock的静态类 class MailSenderTest { @Test void sendMail_success() throws Exception { // Mock静态方法Transport.send PowerMockito.mockStatic(Transport.class); PowerMockito.doNothing().when(Transport.class); Transport.send(any(MimeMessage.class)); boolean result = MailSender.sendMail("me@gmail.com", "hi", "you@gmail.com"); assertEquals(true, result); } }
原有测试代码的问题总结
@Mock和@InjectMocks注解使用错误:应该给测试类MailSender加@InjectMocks,给依赖对象加@Mock- Mockito无法直接Mock
Transport的静态方法,这是核心限制 - 静态方法
sendMail不应通过实例调用,且Mockito无法Mock静态方法内部的静态调用 - 字符串转义错误:
"you@gmail.com"写成了""you@gmail.com""
内容的提问来源于stack exchange,提问作者likejudo
相关产品推荐
相关产品推荐

