Как издеваться над javax.mail.Session

мне нужно издеваться над объектом javax.mail.Session в моих модульных тестах. Класс javax.mail.Session помечен как final, поэтому Mockito не может создать макет. Кто-нибудь знает, как это исправить?

Изменить: мой тест является тестом Arquillian и уже имеет аннотацию @RunWith(Arquillian.class). Поэтому powermock не вариант.


person martin    schedule 21.11.2011    source источник


Ответы (9)


Вы можете немного реорганизовать свой код. В книге «Работа с устаревшим кодом» Мартина Фаулера он описывает метод отделения внешнего API (например, Java Mail API) от кода вашего собственного приложения. Техника называется «Обертывание и кожа» и довольно проста.

Что вы должны сделать, это просто:

  1. Создайте интерфейс MySession (который является вашим собственным материалом), извлекая методы из класса javax.mail.Session.
  2. Реализуйте этот интерфейс, создав конкретный класс (немного похожий на исходный javax.mail.Session).
  3. В каждом методе DELEGATE вызов эквивалентного метода javax.mail.Session
  4. Создайте свой фиктивный класс, который реализует MySession :-)
  5. Обновите производственный код, чтобы использовать MySession вместо javax.mail.Session.

Удачного тестирования!

РЕДАКТИРОВАТЬ: Также взгляните на этот пост в блоге: http://www.mhaller.de/archives/18-How-to-mock-a-ThirdParty-final-class.html

person nowaq    schedule 21.11.2011
comment
Один маленький комментарий. Вы можете решить обернуть только те методы javax.mail.Session, которые вы действительно используете в своем приложении. - person nowaq; 21.11.2011
comment
Для конструктора MimeMessage требуется объект Session. Как бы вы справились с этим? - person Baz; 08.06.2017

Используйте 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");
  }
}
person Boris Pavlović    schedule 21.11.2011
comment
Поскольку я хочу использовать это в тесте Arquillian, я не могу поместить второй TestRunner в свой testCase =) - person martin; 21.11.2011
comment
Вот идея, как запустить тестовый пример, объединяющий два бегуна eclipse.org/forums /index.php/m/708337 - person Boris Pavlović; 21.11.2011
comment
Для тех, кто использует TestNG, вместо @RunWith ваш тест должен расширять PowerMockTestCase. - person RockMeetHardplace; 07.03.2014

Вы можете использовать проект Mock JavaMail. Впервые я нашел его у Косукэ Кавагути. У Алана Францони также есть руководство по этому вопросу в его блоге.

Когда вы помещаете этот jar-файл в свой путь к классам, он заменяет любую отправку почты в почтовые ящики в памяти, которые можно проверить немедленно. Он очень прост в использовании.

Добавление этого в ваш путь к классам — это, по общему признанию, довольно тяжелый способ издеваться над чем-то, но вы все равно редко хотите отправлять настоящие электронные письма в своих автоматических тестах.

person jhericks    schedule 21.11.2011
comment
Ссылка выдает ошибку сертификата. Вот исправленная версия. blogs.java.net/blog/kohsuke/archive/2007/ 04/ - person Raystorm; 10.05.2013
comment
Спасибо, @Raystorm. Однако по вашей ссылке была та же проблема. Я заменил свою ссылку на устаревший блог Kohsuke Kawaguchi ссылкой на настоящий проект Java.net и ссылкой на другое руководство по его использованию. - person jhericks; 10.05.2013
comment
хорошо, как насчет ссылки непосредственно на безопасную версию. blogs.java.net/blog/kohsuke/archive/2007/ 04/ - person Raystorm; 11.05.2013

Если вы хотите смоделировать окончательные классы, вы можете использовать дефинализатор JDave, который можно найти здесь: http://jdave.org/documentation.html#mocking

Он использует CGLib для динамического изменения байт-кода при загрузке JVM для преобразования класса в неокончательный класс.

Затем эту библиотеку можно использовать с JMock2 ( http://www.jmock.org/mocking-classes.html ) для проведения тестов, поскольку, насколько мне известно, Mockito несовместим с JDave.

person DarkRift    schedule 21.11.2011
comment
затем вы можете создать себе насмешку, добавить все свои ожидания и вызвать assertIsSatisfied в конце вашего тестового метода, чтобы проверить, были ли сделаны все ожидаемые вызовы. Хороший пример можно найти по адресу: jmock.org/getting-started.html. отсутствующий вызов - это assertIsSatisfied, потому что он автоматически вызывается с помощью RunWith(JMock), но не обязательно использовать RunWith JMock, вы можете просто сделать assertIsSatisfied в конце метода, а затем вы можете использовать свой @RunWith(Arquillian.class ) - person DarkRift; 21.11.2011

Используйте функции 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] = "[email protected]";
    addresses[1] = "[email protected]";
    String from = "[email protected]";
    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("[email protected]"));
    verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
    verify(message).addFrom(new InternetAddress[]{new InternetAddress("[email protected]")});
    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));
};
person Daniel Hinojosa    schedule 28.03.2015

См. документы PowerMock для работает под JUnit 3, поскольку у него нет исполнителей, или используется инструмент для работы с байт-кодом.

person Dave Newton    schedule 21.11.2011

Если вы можете внедрить 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));
}
person Brad    schedule 24.11.2011

Это довольно старый вопрос, но вы всегда можете реализовать свой собственный транспорт, используя JavaMailAPI. С вашим собственным транспортом вы можете просто настроить его в соответствии с эта документация. Одним из преимуществ такого подхода является то, что вы можете делать с этими сообщениями все, что захотите. Возможно, вы бы сохранили их в хеше/наборе, чтобы затем убедиться, что они действительно были отправлены в ваши модульные тесты. Таким образом, вам не нужно издеваться над конечным объектом, вы просто реализуете свой собственный.

person Lucas    schedule 10.07.2012

Я использую библиотеку mock-javamail. Он просто заменяет исходную реализацию javamail в пути к классам. Вы можете отправлять письма в обычном режиме, они просто отправляются в почтовый ящик в памяти, а не в настоящий почтовый ящик.

Наконец, вы можете использовать объект MailBox для утверждения всего, что хотите проверить.

person qrtt1    schedule 02.05.2013