Как провести модульное тестирование (использовать макеты или классический модульный тест?) - PullRequest
4 голосов
/ 01 марта 2011

Мне нужно написать тест JUnit для следующего метода (упрощенно):

/** Return name of previous entry or name of given entry if no previous entry. */
public String getPreviousName(TreeEntry entry) {
    if (entry.getPrevious() != null) {
        return entry.getPrevious().getName();
    } else {
        return entry.getName();
    }
}

Я просто не знаю, как мне это проверить.Я мог бы либо

a) Использовать Mocks для макетирования параметра и его предыдущей записи, либо

b) Создать настоящую TreeEntry и ее настоящую предыдущую запись

Проблема с использованиемв том, что мне нужно сказать в тестовом примере, какой метод должна использовать фактическая реализация, например, здесь я говорю, что правильное имя получается через getPrevious () TreeEntry, а затем через getName () этой предыдущей записи (не учитываяесли NULL, потому что это в принципе не проблема):

TreeEntry mockEntry = mock(TreeEntry.class);
TreeEntry mockPreviousEntry = mock(TreeEntry.class);
when(mockEntry.getPrevious()).thenReturn(mockPreviousEntry);
when(mockPreviousEntry.getName()).thenReturn("testName");

assertEquals("testName", classUnderTest.getPreviousName(mockEntry));

Но разработчик может, например, также реализовать это так:

return NameService.getName(entry.getPrevious());

Это просто не имеет значения дляtest.

Однако, если я использую классическое модульное тестирование без макетов, у меня возникает другая проблема.Конструктор TreeEntry очень сложен.Он имеет 5 параметров, которые нуждаются в действительных объектах.Поэтому очень сложно создать что-то вроде:

TreeEntry entry = new TreeEntry(...);
TreeEntry previousEntry = new TreeEntry(...);
entry.setPrevious(previousEntry);
previousEntry.setName("testName");

assertEquals("testName", classUnderTest.getPreviousName(entry));

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

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

Что бы вы сказали, как правильно провести модульное тестирование этого?

Я лично склонен кбольше нравится издевательский подход.Можно ли сказать, что в тестовом примере я уже указал, кто является соавторами тестируемого класса, потому что это в некотором роде также больше относится к дизайну, чем к реализации?

С наилучшими пожеланиями, Алекс

Ответы [ 4 ]

1 голос
/ 01 марта 2011

Я думаю, что ваш лучший вариант - извлечь интерфейс из TreeEntry, внедрить его с базовой заглушкой в ​​тестовом коде и протестировать против этого.

1 голос
/ 01 марта 2011

Я думаю, что ваш подход насмешки выглядит просто отлично (и я согласен, что если TreeEntry настолько сложен, как вы говорите, вы не должны смешивать их в этих тестах (но я надеюсь, что вы делаете тест *) 1004 * в другом месте)). Если вы пишете много подобных тестов, вам следует рассмотреть возможность использования некоторого шаблона Builder (с беглым синтаксисом, если у вас есть терпение его создать), чтобы вы могли создать макет, например. как это:

BuildANew.TreeEntry().WithPreviousEntryName("testName");

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

0 голосов
/ 01 марта 2011

Я думаю, что с издевательством все в порядке.

С другой стороны: тест JUnit - это не просто тест, он также управляет дизайном.

Если вам сложно написать тестовый пример, например, конструктор принимает сложные параметры, вы можете переосмыслить свой дизайн.

Кстати, я не думаю, что метод getPreviousName() необходим, getPreviousEntry() достаточно.

0 голосов
/ 01 марта 2011

Если вы хотите проверить только

public String getPreviousName(TreeEntry entry)

метод Я бы посмеялся над этим, поскольку вы только тестируете этот метод. Хорошей практикой является быстрое проведение юнит-тестов.

Но если вы заинтересованы в тестировании всего доступа к файлам и создании вашего TreeEntry, я бы их создал.

В своей повседневной работе я стараюсь сначала написать юнит-тесты, а потом разработать функцию, потому что это помогает создавать простые в использовании / тестировать интерфейсы.

http://en.wikipedia.org/wiki/Test-driven_development

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...