如何模拟javax.mail.Session

时间:2011-11-21 14:33:27

标签: java email mockito

我需要在单元测试中模拟一个javax.mail.Session对象。 类javax.mail.Session标记为final,因此Mockito无法创建模拟。有没有人知道如何解决这个问题?

编辑: 我的测试是一个Arquillian测试,并且已经有一个注释@RunWith(Arquillian.class)。因此,电动摇滚不是一种选择。

9 个答案:

答案 0 :(得分:10)

您可以稍微重构一下代码。在Martin Fowler的“使用遗留代码”一书中,他描述了一种将外部API(想想Java Mail API)与您自己的应用程序代码分离的技术。这种技术被称为“包裹和皮肤”,非常简单。

你应该做的只是:

  1. 通过从javax.mail.Session类中提取方法,创建一个接口MySession(这是你自己的东西)。
  2. 通过创建一个具体的类来实现该接口(看起来有点像原始的javax.mail.Session)
  3. 在每个方法中,DELEGATE调用等效的javax.mail.Session方法
  4. 创建实现MySession的模拟类: - )
  5. 更新您的生产代码以使用MySession而不是javax.mail.Session
  6. 快乐测试!

    编辑:另请查看此博文: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对象断言您要检查的任何内容。