我需要在单元测试中模拟一个javax.mail.Session对象。但是javax.mail.Session类是final的,因此Mockito无法创建模拟对象。有没有人有修复此问题的想法?
编辑: 我的测试是一个Arquillian测试,并且已经有一个注解@RunWith(Arquillian.class)。因此,powermock不是一个选择。
使用PowerMockito进行模拟。
@RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
@PrepareForTest(javax.mail.Session.class)
public class YourTestCase {
@Test
public void test() throws Exception {
PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");
}
}
如果您想模拟一个final类,可以使用JDave unfinalizer,该工具可以在此处找到:http://jdave.org/documentation.html#mocking
它使用CGLib在JVM加载时动态更改字节码,将类转换为非final类。
然后可以将此库与JMock2(http://www.jmock.org/mocking-classes.html)一起使用来进行测试,因为据我所知,Mockito与JDave不兼容。
public class SendEmailGood {
private final Supplier<Message> messageSupplier;
private final Consumer<Message> messageSender;
public SendEmailGood(Supplier<Message> messageSupplier,
Consumer<Message> messageSender) {
this.messageSupplier = messageSupplier;
this.messageSender = messageSender;
}
public void send(String[] addresses, String from,
String subject, String body)
throws MessagingException {
Message message = messageSupplier.get();
for (String address : addresses) {
message.addRecipient
(Message.RecipientType.TO, new InternetAddress(address));
}
message.addFrom(new InternetAddress[]{new InternetAddress(from)});
message.setSubject(subject);
message.setText(body);
messageSender.accept(message);
}
}
那么你的测试代码将会类似于下面的示例:
@Test
public void sendBasicEmail() throws MessagingException {
final boolean[] messageCalled = {false};
Consumer<Message> consumer = message -> {
messageCalled[0] = true;
};
Message message = mock(Message.class);
Supplier<Message> supplier = () -> message;
SendEmailGood sendEmailGood = new SendEmailGood(supplier, consumer);
String[] addresses = new String[2];
addresses[0] = "foo@foo.com";
addresses[1] = "boo@boo.com";
String from = "baz@baz.com";
String subject = "Test Email";
String body = "This is a sample email from us!";
sendEmailGood.send(addresses, from, subject, body);
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("foo@foo.com"));
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("boo@boo.com"));
verify(message).addFrom(new InternetAddress[]{new InternetAddress("baz@baz.com")});
verify(message).setSubject(subject);
verify(message).setText(body);
assertThat(messageCalled[0]).isTrue();
}
Consumer<Message> consumer = message -> {
try {
Transport.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
};
Supplier<Message> supplier = () -> {
Properties properties = new Properties();
return new MimeMessage(Session.getDefaultInstance(properties));
};
我使用mock-javamail库。它只是替换了类路径中原有的javamail实现。你可以正常地发送邮件,但它只会发送到内存MailBox,而不是真正的邮箱。
最后,你可以使用MailBox对象来断言任何你想要检查的内容。
这是一个相当老的问题,但您始终可以使用 JavaMail API来实现自己的传输。通过自己的传输,您可以根据此文档进行配置。采用这种方法的好处之一是,您可以随意处理这些消息。也许您会将它们存储在哈希/集合中,然后可以确保它们在单元测试中实际发送。这样,您就不必模拟最终对象,您只需自己实现即可。
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@Test
public void createAndSendBookChangesMail() {
// Your custom service layer object to test
MailServiceImpl service = new MailServiceImpl();
// MOCK BEHAVIOUR
JavaMailSender mailSender = mock(JavaMailSender.class);
service.setMailSender(mailSender);
// PERFORM TEST
service.createAndSendMyMail("some mail message content");
// ASSERT
verify(mailSender).send(any(SimpleMailMessage.class));
}