Как я могу издеваться над классами, которые трудно создать (javax.mail.Message)? - PullRequest
7 голосов
/ 22 мая 2011

Я хочу использовать тесты junit в моем следующем проекте, но я не уверен, какой из нескольких фиктивных пакетов мне следует использовать. Я также прочитал несколько учебных пособий, но не нашел информации о том, как решить конкретную проблему, изложенную ниже. Возможно, эта функция недоступна в пакетах, которые я проверил.

Вот проблема: я хочу написать класс фильтра электронной почты, который выполняет итерации по List<javax.mail.Message> и фильтрует сообщения электронной почты по теме, дате, от, до и т. Д. Код для проверки выглядит следующим образом:

public List<Message> doFilter(List<Message> messageList) {

    List<Message> newList = new ArrayList<Message>(messageList.size());

    try {
        for (Message message: messageList) {
            if (start != null) {
                Date sentDate = message.getSentDate();
                if (sentDate == null || sentDate.before(start))
                    continue;
            }
            if (end != null) {
                Date receivedDate = message.getReceivedDate();
                if (receivedDate == null || receivedDate.after(end))
                    continue;
            }
            newList.add(message);
        }
    }
    catch(Exception e) {
        e.printStackTrace();
    }
    return newList;
}

Таким образом, очевидный тестовый пример - построить List с несколькими сообщениями и проверить, содержат ли новый список, возвращаемый фильтром, правильные сообщения.

Но javax.mail.Message является абстрактным и не может быть создан непосредственно. Для этого мне нужно было бы создать реальный почтовый магазин, включая имя учетной записи и пароль.

Итак, мои вопросы:

  • Как мне смоделировать несколько javax.mail.Message объектов с разными значениями, чтобы мой класс фильтров мог вызывать message.getSentDate() и другие Message методы, получая значения, которые я определил в коде настройки теста?

  • Какой из макетов пакетов лучше всего подходит для такого рода проблем?

Все ответы действительно приветствуются.

Ответы [ 3 ]

2 голосов
/ 24 мая 2011

Я попробовал оба подхода, чтобы узнать о преимуществах и недостатках:

Подход 1: создайте новый класс MockMessage, который реализует только те методы, которые мне нужны, в моем коде фильтра, а затем напишите стандартные тесты JUnit.

Проблема с отсутствующим абстрактным методом на самом деле тривиальна, так как эти методы могут быть сгенерированы используемой мной IDE (они будут пустыми, но в любом случае они не нужны тестируемому коду). Пакет Mock на самом деле не используется.

public class MockMessage extends Message {

    private Date sentDate;

    public MockMessage(Date sentDate) {
        this.sentDate = sentDate;
    }

    public Date getSentDate() throws MessagingException {
        return sentDate;
    }

    // the rest of the required methods can easily be generated by the IDE
    ...
}


public void testFilter1() {

    try {
        DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss");
        final Date date0 = dtf.parseDateTime("2011-05-19 05:51:26").toDate();
        final Date date1 = dtf.parseDateTime("2011-05-19 05:51:27").toDate();
        final Date date2 = dtf.parseDateTime("2011-05-19 05:51:28").toDate();

        final List<Message> mockMessages = Arrays.asList(
            (Message)new MockMessage(date0),
            (Message)new MockMessage(date1),
            (Message)new MockMessage(date2)
        );

        MessageFilter filter = new ByDate(date1, null);
        List<Message> result = filter.doFilter(mockMessages);
        assertEquals(result.size(),2);
        assertEquals(result.get(0).getSentDate(),date1);
        assertEquals(result.get(1).getSentDate(),date2);
    }
    catch(Exception e) {
        fail(e.getMessage());
    }
}

Подход 2: с использованием jMock и ClassImposteriser. Объем кода немного меньше, чем в подходе 1, и никакого дополнительного класса MockMessage не требуется.

Я все еще предпочитаю первый подход, так как его проще понять. Второй подход может быть полезен в других ситуациях, когда создание объектов гораздо сложнее, чем javax.mail.Message.

public void testFilter2() {

    try {
        DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss");
        final Date date0 = dtf.parseDateTime("2011-05-19 05:51:26").toDate();
        final Date date1 = dtf.parseDateTime("2011-05-19 05:51:27").toDate();
        final Date date2 = dtf.parseDateTime("2011-05-19 05:51:28").toDate();

        Mockery mockery = new Mockery();
        mockery.setImposteriser(ClassImposteriser.INSTANCE);

        final List<Message> mockMessages = Arrays.asList(
            mockery.mock(Message.class, "message 1"),
            mockery.mock(Message.class, "message 2"),
            mockery.mock(Message.class, "message 3")
        );

        mockery.checking(new Expectations() {{
           allowing(mockMessages.get(0)).getSentDate(); will(returnValue(date0));
           allowing(mockMessages.get(1)).getSentDate(); will(returnValue(date1));
           allowing(mockMessages.get(2)).getSentDate(); will(returnValue(date2));
        }});

        MessageFilter filter = new ByDate(date1, null);
        List<Message> result = filter.doFilter(mockMessages);
        assertEquals(result.size(),2);
        assertEquals(result.get(0).getSentDate(),date1);
        assertEquals(result.get(1).getSentDate(),date2);
    }
    catch(Exception e) {
        fail(e.getMessage());
    }
}
0 голосов
/ 22 мая 2011

У большинства фальшивых фреймворков с этим не должно быть проблем, хотя тот факт, что вы имитируете класс, а не интерфейс, делает его немного более неловким.

Вот простой пример использования JMock (фреймворк, которым ябольшинство знакомых с):

Mockery mockery = new Mockery();
mockery.setImposteriser(ClassImposteriser.INSTANCE);

final List<Message> mockMessages = asList(
    mockery.mock(Message.class, "message1"),
    mockery.mock(Message.class, "message2")
);

mockery.checking(new Expectations() {{
   allowing(mockMessages.get(0)).getSubject(); will(returnValue("some subject"));
   allowing(mockMessages.get(1)).getSubject(); will(returnValue("some other subject"));
}});

Обычно, я бы не рекомендовал людям издеваться над подобными вещами и просто создавать объекты напрямую, но API JavaMail настолько неудобен в использовании, что понятно, что он не хочетсделать это.

0 голосов
/ 22 мая 2011

Просто создайте класс, MockMessage, который расширяет Message.Затем вы можете реализовать или переопределить поведение, которое хотите макетировать.

Что касается фальшивых фреймворков, я обнаружил, что этих проблем больше, чем они того стоят, но YMMV.

...