Модульное тестирование: что тестировать / что не тестировать? - PullRequest
19 голосов
/ 24 января 2012

С тех пор, как несколько дней назад я начал интересоваться модульным тестированием и TDD в C # и VS2010.Я читал посты в блогах, смотрел учебные пособия по YouTube и многое другое, объясняющее, почему TDD и модульное тестирование так хороши для вашего кода, и как это сделать.

Но самая большая проблема, которую я нахожу, заключается в том, чтоЯ не знаю, что проверять в своих тестах, а что нет.

Я понимаю, что должен проверять все логические операции, проблемы со ссылками и зависимостями, но, например, должен ли я создавать модульный тестдля форматирования строки, которая должна быть введена пользователем?Или это просто напрасно тратит мое время, пока я просто могу проверить это в реальном коде?

Ответы [ 8 ]

18 голосов
/ 24 января 2012

В TDD каждая строка кода должна быть подтверждена неудачным тестом, написанным перед кодом.

Это означает, что вы не можете разработать любой код без тестового случая. Если у вас есть строка кода (условие, ветвь, присваивание, выражение, константа и т. Д.), Которую можно изменить или удалить без провала какого-либо теста, это означает, что эта строка кода бесполезна, и должно быть * 1008. * удален (или у вас отсутствует тест для подтверждения его существования).

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

ОБНОВЛЕНИЕ (вариант использования, предложенный Ред. ):

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

Вот контрпример, вы будете удивлены, насколько сложно определить ошибки копирования и вставки и насколько они распространены:

private Set<String> inclusions = new HashSet<String>();
private Set<String> exclusions = new HashSet<String>();

public void include(String item) {
    inclusions.add(item);
}

public void exclude(String item) {
    inclusions.add(item);
}

С другой стороны, тестирование include() и exclude() методов само по себе является излишним, потому что они сами по себе не представляют никаких вариантов использования. Тем не менее, они, вероятно, являются частью бизнес-прецедента, вместо этого вам следует протестировать.

Очевидно, что вы не должны проверять, действительно ли x в x = 7 7 после назначения. Также тестирование сгенерированных геттеров / сеттеров является излишним. Но это самый простой код, который часто ломается. Слишком часто из-за ошибок копирования и вставки или опечаток (особенно в динамических языках).

Смотри также:

14 голосов
/ 24 января 2012

Ваши первые несколько проектов TDD, вероятно, приведут к худшему дизайну / редизайну и потребуется больше для завершения в процессе обучения (по крайней мере, по моему опыту).Вот почему вам не следует переходить на использование TDD в большом критическом проекте.

Мой совет - использовать «чистый» TDD (приемочный / модульный тест всего тестового в первую очередь) на несколько небольших проектов (100-10 000 LOC).Либо выполняйте побочные проекты самостоятельно, либо, если вы не пишете код в свободное время, используйте TDD для небольших внутренних служебных программ для своей работы.

После того, как вы выполните "чистый" TDD для примерно 6-12 проектоввы начнете понимать, как TDD влияет на дизайн, и узнаете, как проектировать для тестируемости .Как только вы научитесь проектировать тестируемость, вам потребуется меньше TDD и максимизировать ROI тестов юнитов, регрессии, приемки и т. Д., А не тестировать все заранее.

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

5 голосов
/ 24 января 2012

Нет простого ответа на этот вопрос. Существует закон убывающей отдачи в действии, поэтому достижение идеального охвата редко стоит того. Знание того, что тестировать - это опыт, а не правила. Лучше осознанно оценивать процесс на ходу. Что-то сломалось? Было ли возможно проверить? Если нет, то можно ли переписать код, чтобы сделать его более тестируемым? Стоит ли всегда проверять подобные случаи в будущем?

Если вы разделите свой код на модели, представления и контроллеры, вы обнаружите, что большая часть критического кода находится в моделях, и они должны быть достаточно тестируемыми. (Это один из основных моментов MVC.) Если фрагмент кода важен, я проверяю его, даже если это означает, что мне придется переписать его, чтобы сделать его более тестируемым. Если фрагмент кода легко ошибиться или сломаться в результате будущих обновлений, он проходит тестирование. Я редко тестирую контроллеры и представления, так как это не стоит для меня проблем.

4 голосов
/ 24 января 2012

Как я вижу, весь ваш код попадает в одну из трех групп:

  1. Код, который легко протестировать: сюда входят ваши собственные детерминированные публичные методы.
  2. Код, который сложно протестировать: сюда входят GUI, недетерминированные методы, приватные методы и методы со сложной настройкой.
  3. Код, который вы не хотите тестировать: он включает в себя сторонний код и код, который сложно протестировать и не стоит усилий.

Из трех, вы должны сосредоточиться на тестировании easy code . трудный для тестирования кода должен быть реорганизован так, чтобы он состоял из двух частей: кода, который вы не хотите тестировать, и простого кода. И, конечно же, вы должны протестировать переработанный простой код.

3 голосов
/ 24 января 2012

Кент Бек в объяснении «Экстремальное программирование» сказал, что вам нужно только тестировать то, что нужно для работы в производстве.

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

2 голосов
/ 05 мая 2015

Я думаю, что вы должны только тестировать точки входа в поведение системы. Это включает открытые методы, открытые методы доступа и открытые поля, но не константы (константные поля, перечисления, методы и т. Д.). Он также включает в себя любой код, который напрямую связан с IO, поэтому я объясню, почему ниже.

Мои рассуждения таковы:

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

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

  3. Константы по конструкции - это не поведение, а аксиомы. Модульный тест, который проверяет константу, сам по себе является константой, поэтому для написания теста на константы потребуется только дублированный код.

Итак, чтобы ответить на ваш конкретный пример:

я должен создать модульный тест для форматирования строки быть введенным пользователем?

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

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

ПРИМЕЧАНИЕ. Вы можете написать временные модульные тесты для непубличных вещей, чтобы убедиться, что ваша реализация работает. Это скорее способ помочь вам понять, как правильно его реализовать, и убедиться, что ваша реализация работает так, как вы хотели. После того, как вы проверили, что он работает, вы должны удалить модульный тест или отключить его из своего набора тестов.

1 голос
/ 24 января 2012

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

  • запись теста (он не должен пройти) КРАСНЫЙ
  • написать код для выполнения теста ЗЕЛЕНЫЙ
  • рефакторинг вашего кода

Итак, вопрос "Что я должен проверить?" имеет ответ типа: «Вы должны написать тест, который соответствует функции или определенным требованиям».

Таким образом, вы получаете обязательное покрытие кода, а также лучший дизайн кода (помните, что TDD также означает «дизайн», управляемый тестами).

Вообще говоря, вы должны протестировать ВСЕ публичные методы / интерфейсы.

0 голосов
/ 24 января 2012

я должен создать модульный тест для форматирования строки, который может быть введен пользователем?Или это просто тратит мое время, пока я просто могу проверить это в реальном коде?

Не уверен, что я понимаю, что вы имеете в виду, но тесты, которые вы пишете в TDD, должны проверять ваш рабочий код.Это не тесты, которые проверяют пользовательский ввод.

Другими словами, могут быть модульные тесты TDD, которые проверяют правильность пользовательского ввода код , но не может быть блок TDDтесты, которые проверяют сам пользовательский ввод.

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