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