我需要在单元测试中模拟一个javax.mail.Session对象。 类javax.mail.Session标记为final,因此Mockito无法创建模拟。有没有人知道如何解决这个问题?
编辑: 我的测试是一个Arquillian测试,并且已经有一个注释@RunWith(Arquillian.class)。因此,电动摇滚不是一种选择。
答案 0 :(得分:10)
您可以稍微重构一下代码。在Martin Fowler的“使用遗留代码”一书中,他描述了一种将外部API(想想Java Mail API)与您自己的应用程序代码分离的技术。这种技术被称为“包裹和皮肤”,非常简单。
你应该做的只是:
快乐测试!
编辑:另请查看此博文:http://www.mhaller.de/archives/18-How-to-mock-a-thirdparty-final-class.html
答案 1 :(得分:6)
使用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");
}
}
答案 2 :(得分:5)
您可以使用Mock JavaMail项目。我首先从Kohsuke Kawaguchi找到它。 Alan Franzoni在他的博客上也有一个primer for it。
当您将此jar文件放入类路径时,它会将任何邮件发送替换为可以立即检查的内存邮箱。它非常易于使用。
将此添加到您的类路径无疑是一种非常严厉的模拟方法,但您很少想在自动化测试中发送真实的电子邮件。
答案 3 :(得分:1)
如果你想模拟最终的类,你可以使用JDave unfinalizer,它可以在这里找到:http://jdave.org/documentation.html#mocking
它使用CGLib在加载JVM时动态地改变字节码,以将类转换为非最终类。
然后可以将此库与JMock2(http://www.jmock.org/mocking-classes.html)一起使用来进行测试,因为据我所知,Mockito与JDave不兼容。
答案 4 :(得分:1)
使用Java 8函数!
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));
};
答案 5 :(得分:0)
请参阅running under JUnit 3的PowerMock文档,因为它没有跑步者,或者使用字节码操作工具。
答案 6 :(得分:0)
如果您可以将Spring引入您的项目,您可以使用JavaMailSender并模拟它。我不知道你的要求有多复杂。
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));
}
答案 7 :(得分:0)
这是一个非常古老的问题,但您始终可以使用JavaMail API实现自己的传输。使用您自己的传输,您可以根据this documentation进行配置。这种方法的一个好处是你可以用这些消息做任何你想做的事情。也许你会将它们存储在一个哈希/集合中,然后你可以确保它们实际上已经在你的单元测试中发送了。这样,您不必模拟最终对象,只需实现自己的。
答案 8 :(得分:0)
我使用mock-javamail库。它只是替换类路径中的原始javamail实现。您可以正常发送邮件,它只是发送到内存中的MailBox,而不是真正的邮箱。
最后,您可以使用MailBox对象断言您要检查的任何内容。