TDD: Какие методы вы используете для модульного тестирования?
Существует один аспект TDD, который я никогда не понимал полностью.
Предположим, кто-то попросил вас реализовать простой объект Stack. Если вы правильно разработали свой дизайн, вы получите минимальный и чистый API. Предположим: push()
, pop()
и isEmpty()
. Все, что больше, чем чрезмерное убийство спроса, и позволяет пользователю слишком много места, чтобы возиться с вашим кодом.
Итак, теперь давайте предположим, что вы хотите unit test ваш код. Как вы это делаете, если все ваши общедоступные методы - это только три показанные выше? Эти методы будут проходить тестирование до сих пор.
Таким образом, вы добавляете частные методы, которые вам совсем не помогают, поскольку они не видны в вашем случае unit test. Или вы делаете эти методы общественности и там идет ваш минималистичный API, который вы работали так тяжело. Теперь пользователь будет путаться со своим стеком, и ошибки обязательно появятся.
Как вы справляетесь с этой дилеммой для открытия общедоступных методов тестирования против чистого и простого API?
Изменить:, чтобы указать в правильном направлении. Было бы неплохо получить технические указатели (например, "использовать этот хак, чтобы открыть частные методы" и т.д.), но я очень более интересны более общие ответы относительно того, что из двух понятий более важно, и как вы подходите к этому вопросу.
Ответы
Ответ 1
-
проверить функции; это обычно означает тестирование открытого интерфейса - ведь не все функции должны быть доступны через открытый интерфейс? если они не являются, то они не являются функциями! Там могут быть исключения, но я не могу придумать.
-
проверить открытый интерфейс; любые методы, которые не вызываются прямо или косвенно из открытого интерфейса, не нужны. Мало того, что они не нуждаются в проверке, им вообще не нужно существовать.
Ответ 2
Вы должны взглянуть на этот вопрос: проверить личный метод?.
Чтобы не сломать инкапсуляцию, я обнаружил, что частный метод является огромным или сложным или достаточно важным, чтобы требовать его собственные тесты, я просто помещал его в другой класс и делал его общедоступным (объект метода). Затем я могу легко протестировать ранее открытый, но теперь общедоступный метод, который теперь живет на собственном классе.
Ответ 3
Используя ваш пример Stack, вам действительно не нужно подвергать любые внутренние работы unit test его. Повторяя то, что говорили другие, вы должны быть свободны, чтобы иметь столько частных методов, сколько необходимо, но только проверяйте свой публичный API.
[Test]
public void new_instance_should_be_empty()
{
var stack = new Stack();
Assert.That(stack.IsEmpty(), Is.True);
}
[Test]
public void single_push_makes_IsEmpty_false()
{
var stack = new Stack();
stack.Push("first");
Assert.That(stack.IsEmpty(), Is.False);
}
Используя комбинацию pushing и popping, вы можете моделировать все поведение класса Stack, потому что это единственный способ, которым пользователь должен взаимодействовать с ним. Вам не нужны какие-либо трюки, потому что вы должны тестировать только то, что могут использовать другие.
[Test]
public void three_pushes_and_three_pops_results_in_empty_stack()
{
var stack = new Stack();
stack.Push("one");
stack.Push("two");
stack.Push("three");
stack.Pop();
stack.Pop();
stack.Pop();
Assert.That(stack.IsEmpty(), Is.True);
}
Ответ 4
Иногда я делаю методы, которые в противном случае были бы частными в методах уровня пакета (Java) или внутренних (.NET), обычно с комментарием или аннотацией/атрибутом, чтобы объяснить, почему.
В большинстве случаев я избегаю этого. Что публичный API не позволит вам протестировать в вашем случае стека? Если пользователь может увидеть ошибку, и они используют только открытый API, что мешает вам совершать одни и те же вызовы?
Время, которое я предоставляю в частном порядке в частном порядке, - это когда он упрощает тестирование одной части сложного набора шагов в изоляции - если один вызов метода очень "короток", может оказаться очень полезным проверить каждый шаг в если эти шаги не должны быть видны обычным пользователям.
Ответ 5
right TDD - это все о тестировании поведения, которое может быть проверено публичным интерфейсом... если есть какие-либо частные методы, то эти методы должны быть косвенно протестированы любым открытым интерфейсом...
Ответ 6
С TDD весь ваш код должен быть доступен из вашего открытого интерфейса:
- Сначала вы пишете тесты для своих функций.
- Затем вы пишете минимальный код для прохождения тестов. Это признак того, что ваши функции реализованы.
Если какой-либо код не распространяется, это означает, что либо этот код бесполезен (удалите его и снова запустите свои тесты), либо ваши тесты неполны (некоторые функции были реализованы, но не определены явно тестом).
Ответ 7
Вы можете проверить конфиденциальность с помощью Reflection, но это будет боль позже. Я думаю, вам нужно поставить свой тест в ту же сборку (или пакет) и попытаться использовать Internal. Таким образом, у вас есть защита, и вы можете протестировать свои материалы, которые хотите проверить.
Ответ 8
Если ваш частный метод не опосредованно проверен общедоступными методами тестирования API, и его необходимо протестировать, я бы делегировал ваш основной класс другому второстепенному классу.
public Stack {
public ... push(...) {...}
public ... pop(...) {...}
public ... isEmpty(...) {...}
// secondary class
private StackSupport stackSupport;
public StackSupport getStackSupport() {...}
public void setStackSupport(StackSupport stackSupport) {...}
}
public StackSupport {
public ...yourOldPrivateMethodToTest(...) {...}
}
Затем ваш частный метод является общедоступным методом в другом классе. И вы можете протестировать этот открытый метод в тестах другого класса.: -)
Ответ 9
При кодировании с использованием TDD я создаю API открытого интерфейса для объекта. В вашем примере это означает, что мой интерфейс, который реализует класс, будет иметь только push()
, pop()
и isEmpty()
.
Тем не менее тестирование этих методов путем их вызова не является самодельными тестами сами по себе (подробнее об этом в конце этого сообщения), поскольку они, скорее всего, проверяют сотрудничество нескольких внутренних методов, которые вместе произведите желаемый результат, и в этом ваш вопрос: должны ли эти методы быть частными?
Мой ответ - нет, используйте protected
(или его эквивалент на выбранном вами языке) вместо private
, что означает, что если ваш проект и тестовые POM структурированы аналогично, класс тестового набора может см. внутри фактического класса, так как они фактически находятся в одной папке. Их можно рассматривать как единичные тесты: вы тестируете функциональные блоки самого класса, а не их взаимодействие.
Что касается тестирования отдельных интерфейсов/методов API, то, конечно же, важно это сделать, и я не оспариваю это, однако они все-таки падают где-то между туманной линией приемочные испытания.
На мой взгляд, самое важное, что следует помнить здесь, это то, что модульные тесты говорят вам, что метод неверно работает, приемочные тесты показывают, связано ли сотрудничество с неправильным тестированием и тестированием нескольких методов/классов, неправильная работа.