TDD: Какие методы вы выставляете для модульного тестирования? - PullRequest
16 голосов
/ 22 декабря 2008

Есть один аспект TDD, который я никогда полностью не понимал.

Предположим, кто-то попросил вас реализовать простой объект Stack. Если вы правильно сделали свой дизайн, вы получите очень минимальный и чистый API. Предположим: push(), pop() и isEmpty(). Что-то большее, чем это, чрезмерно убивает спрос и дает пользователю слишком много места, чтобы возиться с вашим кодом.

Итак, давайте предположим, что вы хотите выполнить модульное тестирование своего кода. Как вы поступите так, если все ваши публичные методы - только три, показанные выше? Эти методы пройдут ваше тестирование только до сих пор.

То есть вы либо добавляете приватные методы, которые вам совсем не помогают, так как они не видны в вашем модульном тесте. Или вы делаете эти методы общедоступными, и появляется минималистичный API, над которым вы так усердно работали. Теперь пользователь будет связываться с вашим стеком, и ошибки обязательно придут.

Как вы справляетесь с этой дилеммой открытия открытых методов для тестирования по сравнению с чистым и простым API?

Редактировать: просто чтобы указать в правильном направлении, было бы неплохо получить технические указатели (такие как «использовать этот хак, чтобы раскрыть частные методы» и т. Д.), Но я гораздо больше заинтересованы в более общих ответах относительно того, какое из этих двух понятий более важно, и как вы подходите к этому вопросу.

Ответы [ 9 ]

14 голосов
/ 22 декабря 2008
  1. проверка функций; это обычно означает тестирование открытого интерфейса - не все ли функции должны быть доступны через открытый интерфейс? если нет, то это не черты! Могут быть исключения из этого, но я не могу думать ни о каких.

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

6 голосов
/ 22 декабря 2008

Вам следует взглянуть на этот вопрос: тестируете ли вы приватный метод? .

Чтобы не нарушать инкапсуляцию, я обнаружил, что закрытый метод является огромным, сложным или достаточно важным, чтобы требовать собственных тестов, я просто помещаю его в другой класс и делаю его общедоступным (метод объекта). Затем я могу легко протестировать ранее приватный, но теперь публичный метод, который теперь живет в своем собственном классе.

4 голосов
/ 22 декабря 2008

Используя ваш пример стека, вам действительно не нужно раскрывать какие-либо внутренние механизмы для его модульного тестирования. Повторяя сказанное другими, вы можете свободно использовать столько закрытых методов, сколько нужно, но тестировать только через общедоступный 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);
}

Используя комбинацию нажатия и выталкивания, вы можете смоделировать все поведение класса 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);
}
2 голосов
/ 22 декабря 2008

При использовании TDD весь ваш код должен быть доступен через общедоступный интерфейс:

  • Сначала вы пишете тесты для своих функций.
  • Затем вы пишете минимальный объем кода для прохождения ваших тестов. Это признак того, что ваши функции реализованы.

Если какой-то код не охватывается, это означает, что либо этот код бесполезен (удалите его и запустите ваши тесты снова), либо ваши тесты будут неполными (некоторые функции были реализованы, но тестом явно не определены).

2 голосов
/ 22 декабря 2008

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

2 голосов
/ 22 декабря 2008

Иногда я создаю методы, которые в противном случае были бы частными для методов уровня пакета (Java) или внутренних (.NET), обычно с комментарием или аннотацией / атрибутом для объяснения причины.

Однако в большинстве случаев я избегаю этого. Что бы публичный API не позволял вам тестировать в вашем стековом случае? Если пользователь видит ошибку и использует только общедоступный API, что мешает вам совершать одни и те же вызовы?

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

0 голосов
/ 22 декабря 2008

Если ваш приватный метод не тестируется косвенно методами публичного 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(...) {...}
}

Тогда ваш приватный метод является публичным методом в другом классе. И вы можете протестировать этот публичный метод в тестах другого класса. : -)

0 голосов
/ 22 декабря 2008

При кодировании с использованием TDD я создаю открытый интерфейс API для объекта. В вашем примере это означало бы, что мой интерфейс, который реализует класс, будет иметь только push(), pop() и isEmpty().

Однако тестирование этих методов путем вызова их сами по себе не является модульными тестами (подробнее об этом в конце этого поста) , поскольку они, скорее всего, проверяют взаимодействие нескольких внутренних методов, которые вместе дают желаемый результат, и вот в чем ваш вопрос: должны ли эти методы быть закрытыми?

Мой ответ - нет, используйте protected (или эквивалент его на выбранном вами языке) вместо private, что означает, что если ваш проект и тестовые POM структурированы аналогично, класс тестового набора может посмотрите внутри самого класса, так как они фактически находятся в одной папке. Это можно считать модульными тестами: вы тестируете функциональные блоки самого класса, а не их взаимодействие.

Что касается тестирования отдельных методов интерфейса / API, то, конечно же, важно сделать это, и я не оспариваю это, однако они находятся где-то между туманной линией модульного тестирования и принятия тестирование .

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

0 голосов
/ 22 декабря 2008

Вы можете проверить приватность с помощью Reflection, но позже это станет проблемой. Я думаю, что вам нужно поместить свой тест в ту же сборку (или пакет) и попытаться использовать Internal. Таким образом, у вас есть некоторая защита, и вы можете проверить свои вещи, которые вы хотите проверить.

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