Подтвердить что ты не робот

Unit Testing: что тестировать/что не тестировать?

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

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

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

Есть ли какое-либо руководство, чтобы прояснить эту проблему?

4b9b3361

Ответ 1

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

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

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

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

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

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

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 после назначения. Также тестирование полученных геттеров/сеттеров является чрезмерным. Но это самый простой код, который часто ломается. Слишком часто из-за ошибок копирования и опечаток (особенно в динамических языках).

См. также:

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

Кент Бек, в разделе Extreme Programming Explained, сказал, что вам нужно только проверить, что нужно для работы в производстве.

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

Ответ 5

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

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

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

Ответ 6

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

  • написать тест (он должен завершиться неудачно) RED
  • написать код для удовлетворения теста GREEN
  • реорганизуйте свой код

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

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

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

Ответ 7

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

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

  • Все, что публикует, в основном, указывает на поведение системы. Поэтому следует написать unit test, который гарантирует, что ожидаемое поведение этой точки входа работает по мере необходимости. Вы не должны проверять все возможные способы вызова точки входа, только те, которые вы явно требуете. Таким образом, ваши модульные тесты также являются характеристиками того, какое поведение поддерживает ваша система, и вашей документации о том, как ее использовать.

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

  • Константы по дизайну - это не поведение, а аксиомы. A unit test, который проверяет константу, сам по себе является константой, поэтому это будет только дублированный код и бесполезное усилие, чтобы написать тест для констант.

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

должен ли я создать unit test для форматирования строк, для ввода пользователем?

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

So unit test public methods, public fields, public accessors, даже если это статические конструкции, а также unit test все, что принимает или отправляет данные от внешнего актора, будь то пользователь, база данных, протокол и т.д..

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

Ответ 8

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

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

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