Разработка через тестирование
Часть серии о |
Разработка программного обеспечения |
---|
Разработка через тестирование ( TDD ) — это способ написания кода , который включает в себя написание автоматизированного на уровне модуля тестового примера , который терпит неудачу, затем написание кода, достаточного для прохождения теста, затем рефакторинг как тестового, так и рабочего кода, а затем повторение с еще одним новым тестовым примером.
Альтернативный подход к написанию автоматических тестов — написать весь рабочий код перед началом тестового кода или написать весь тестовый код перед началом рабочего кода. В TDD оба пишутся вместе, что сокращает время отладки. [ 1 ]
, основанной на тестировании TDD связано с концепцией экстремального программирования , начавшейся в 1999 году. [ 2 ] но в последнее время вызвал более общий интерес сам по себе. [ 3 ]
Программисты также применяют эту концепцию для улучшения и отладки устаревшего кода, разработанного с использованием старых технологий. [ 4 ]
История
[ редактировать ]Инженер-программист Кент Бек , которому приписывают разработку или «заново открытие» [ 5 ] В 2003 году было заявлено, что TDD поощряет простые конструкции и внушает доверие. [ 6 ]
Первоначальное описание TDD было в древней книге по программированию. В нем говорилось, что вы берете входную ленту, вручную вводите ожидаемую выходную ленту, а затем программируете до тех пор, пока фактическая выходная лента не будет соответствовать ожидаемому результату. После того, как я написал первую платформу xUnit на Smalltalk, я вспомнил, что прочитал это, и опробовал ее. Для меня это было источником TDD. Описывая TDD программистам старшего возраста, я часто слышу: «Конечно. А как еще можно программировать?» Поэтому я называю свою роль «зановым открытием» TDD.
— Кент Бек , Почему Кент Бек говорит о «повторном открытии» разработки через тестирование? Какова история разработки через тестирование до повторного открытия Кента Бека?, Кент Бек (11 мая 2012 г.). «Почему Кент Бек говорит о «повторном открытии» разработки через тестирование?» . Проверено 1 декабря 2014 г.
Цикл кодирования
[ редактировать ]Шаги TDD несколько различаются в зависимости от автора по количеству и описанию, но в целом они следующие. Они основаны на книге «Разработка через тестирование на примере» . [ 6 ] Кента Бека и статья о Canon TDD .
- 1. Перечислите сценарии использования новой функции.
- Перечислите ожидаемые варианты нового поведения. «Есть базовый случай, а затем что, если время ожидания этой службы истечет, и что, если ключа еще нет в базе данных и…» Разработчик может узнать эти спецификации, задав вопросы о вариантах использования и историях пользователей . Ключевое преимущество TDD заключается в том, что он заставляет разработчика сосредоточиться на требованиях перед написанием кода. Это контрастирует с обычной практикой, когда модульные тесты пишутся только после кода.
- 2. Напишите тест для пункта из списка.
- Напишите автоматический тест, который пройдет , если будет соблюден вариант нового поведения.
- 3. Запустите все тесты. Новый тест должен провалиться – по ожидаемым причинам
- Это показывает, что для желаемой функции действительно необходим новый код. Он подтверждает тестового оборудования правильность работы . Это исключает возможность того, что новый тест ошибочен и всегда пройдет успешно.
- 4. Напишите простейший код, который пройдет новый тест
- Неэлегантный код и жесткое кодирование приемлемы. Код будет доработан на шаге 6. Никакой код, выходящий за пределы тестируемой функциональности, добавлять нельзя.
- 5. Теперь все тесты должны пройти.
- Если какие-либо тесты не пройдены, исправьте неудачные тесты с минимальными изменениями, пока все не пройдут.
- 6. При необходимости выполните рефакторинг, гарантируя, что все тесты продолжают проходить.
- Код подвергается рефакторингу для удобства чтения и сопровождения. В частности, жестко запрограммированные тестовые данные должны быть удалены из производственного кода. Запуск набора тестов после каждого рефакторинга гарантирует, что ни одна существующая функциональность не будет нарушена. Примеры рефакторинга:
- перемещение кода туда, где ему наиболее логично принадлежать
- удаление дублирующего кода
- самодокументирование имен
- разделение методов на более мелкие части
- перестановка иерархии наследования
- Повторить
- Повторяйте процесс, начиная с шага 2, для каждого теста в списке, пока все тесты не будут реализованы и пройдены.
Каждый тест должен быть небольшим, а коммиты должны делаться часто. Если новый код не проходит некоторые тесты, программист может отменить или вернуться, а не проводить чрезмерную отладку .
При использовании внешних библиотек важно не писать тесты настолько маленького размера, чтобы эффективно тестировать только саму библиотеку. [ 3 ] если только нет оснований полагать, что библиотека содержит ошибки или недостаточно многофункциональна, чтобы удовлетворить все потребности разрабатываемого программного обеспечения.
Тестовая работа
[ редактировать ]TDD был принят за пределами разработки программного обеспечения как в продуктовых, так и в сервисных группах, как работа, основанная на тестировании . [ 7 ] Чтобы тестирование было успешным, его необходимо практиковать на микро- и макроуровне. Каждый метод в классе, каждое значение входных данных, сообщение журнала и код ошибки, а также другие точки данных, должны быть протестированы. [ 8 ] Подобно TDD, команды, не связанные с программным обеспечением, перед началом работы разрабатывают проверки контроля качества (QC) (обычно ручные, а не автоматические тесты) для каждого аспекта работы. Эти проверки качества затем используются для информирования о дизайне и подтверждения соответствующих результатов. Шесть шагов последовательности TDD применяются с небольшими семантическими изменениями:
- «Добавить проверку» заменяет «Добавить тест»
- «Выполнить все проверки» заменяет «Выполнить все тесты».
- «Сделать работу» заменяет «Написать код».
- «Выполнить все проверки» заменяет «Выполнить тесты».
- «Навести порядок» заменяет «Рефакторинг кода».
- "Повторить"
Стиль разработки
[ редактировать ]Существуют различные аспекты использования разработки через тестирование, например, принципы «будьте проще, глупее» ( KISS ) и « вам это не понадобится » (YAGNI). Сосредоточив внимание на написании только кода, необходимого для прохождения тестов, проекты часто могут быть чище и понятнее, чем это достигается другими методами. [ 6 ] В книге «Разработка через тестирование на примерах » Кент Бек также предлагает принцип « Притворяйся, пока не сделаешь это ».
Чтобы реализовать некоторую продвинутую концепцию дизайна, такую как шаблон проектирования , пишутся тесты, которые генерируют этот дизайн. Код может оставаться проще целевого шаблона, но при этом проходить все необходимые тесты. Поначалу это может беспокоить, но позволяет разработчику сосредоточиться только на том, что важно.
Сначала напишите тесты. Тесты следует писать до начала тестирования функциональности. Утверждается, что это имеет множество преимуществ. Это помогает гарантировать, что приложение написано с учетом возможности тестирования, поскольку разработчики должны продумать, как тестировать приложение с самого начала, а не добавлять его позже. Это также гарантирует, что будут написаны тесты для каждой функции. Кроме того, написание тестов в первую очередь приводит к более глубокому и раннему пониманию требований к продукту, обеспечивает эффективность тестового кода и позволяет постоянно уделять внимание качеству программного обеспечения . [ 9 ] При написании кода, ориентированного на функцию, разработчики и организации склонны подталкивать разработчика к следующей функции, даже полностью игнорируя тестирование. Первый тест TDD может сначала даже не скомпилироваться, поскольку требуемые для него классы и методы могут еще не существовать. Тем не менее, этот первый тест служит началом спецификации исполняемого файла. [ 10 ]
Каждый тестовый пример изначально терпит неудачу: это гарантирует, что тест действительно работает и может обнаружить ошибку. Как только это будет показано, можно будет реализовать базовую функциональность. Это привело к появлению «мантры разработки через тестирование»: «красный/зеленый/рефакторинг», где красный означает неудачу , а зеленый означает успешный результат . Разработка через тестирование постоянно повторяет этапы добавления неудачных тестовых примеров, их прохождения и рефакторинга. Получение ожидаемых результатов тестирования на каждом этапе укрепляет мысленную модель кода разработчика, повышает уверенность и повышает производительность.
Видимость кода
[ редактировать ]Тестовому коду необходим доступ к коду, который он тестирует, но тестирование не должно ставить под угрозу обычные цели проектирования, такие как сокрытие информации , инкапсуляция и разделение задач . Поэтому код модульного теста обычно находится в том же проекте или модуле , что и тестируемый код.
В объектно-ориентированном проектировании это по-прежнему не обеспечивает доступ к частным данным и методам. Поэтому для модульных тестов может потребоваться дополнительная работа. В Java и других языках разработчик может использовать отражение для доступа к частным полям и методам. [ 11 ] В качестве альтернативы можно использовать внутренний класс для хранения модульных тестов, чтобы они могли видеть члены и атрибуты включающего класса. В .NET Framework и некоторых других языках программирования частичные классы могут использоваться для предоставления частных методов и данных для доступа тестов.
Важно, чтобы такие хаки тестирования не оставались в рабочем коде. В C и других языках директивы компилятора , такие как #if DEBUG ... #endif
могут быть размещены вокруг таких дополнительных классов и всего остального кода, связанного с тестированием, чтобы предотвратить их компиляцию в выпущенный код. Это означает, что выпущенный код не совсем совпадает с тем, что было протестировано. Регулярное выполнение меньшего количества, но более полных, сквозных интеграционных тестов в финальной сборке релиза может гарантировать (помимо прочего), что не существует производственного кода, который тонко полагается на аспекты тестового оборудования.
Среди практиков TDD ведутся споры, документированные в их блогах и других публикациях, относительно того, разумно ли вообще тестировать частные методы и данные. Некоторые утверждают, что частные члены — это всего лишь деталь реализации, которая может измениться, и что это должно быть разрешено без нарушения количества тестов. Таким образом, должно быть достаточно протестировать любой класс через его открытый интерфейс или через интерфейс его подкласса, который в некоторых языках называется «защищенным» интерфейсом. [ 12 ] Другие говорят, что важнейшие аспекты функциональности могут быть реализованы в частных методах, и их непосредственное тестирование дает преимущество более мелких и прямых модульных тестов. [ 13 ] [ 14 ]
Фейки, макеты и интеграционные тесты
[ редактировать ]Модульные тесты названы так потому, что каждый из них тестирует одну единицу кода. В сложном модуле может быть тысяча модульных тестов, а в простом — только десять. Модульные тесты, используемые для TDD, никогда не должны пересекать границы процессов в программе, не говоря уже о сетевых соединениях. Это приводит к задержкам, из-за которых тесты выполняются медленно и отбивают у разработчиков желание запускать весь пакет. Введение зависимостей от внешних модулей или данных также превращает модульные тесты в интеграционные тесты . Если один модуль плохо себя ведет в цепочке взаимосвязанных модулей, не так сразу понятно, где искать причину сбоя.
Когда разрабатываемый код опирается на базу данных, веб-службу или любой другой внешний процесс или службу, принудительное разделение для модульного тестирования также является возможностью и движущей силой для разработки более модульного, более тестируемого и многократно используемого кода. [ 15 ] Необходимо два шага:
- Всякий раз, когда в окончательном проекте требуется внешний доступ, следует определить интерфейс , описывающий доступный доступ. См. принцип инверсии зависимостей для обсуждения преимуществ этого независимо от TDD.
- Интерфейс должен быть реализован двумя способами, один из которых действительно обращается к внешнему процессу, а другой является поддельным или макетным . Поддельным объектам нужно сделать немного больше, чем добавить сообщение, например «Объект Person сохранен», в журнал трассировки тестовое утверждение , для которого можно запустить для проверки правильного поведения. Мок-объекты отличаются тем, что сами содержат тестовые утверждения , которые могут привести к сбою теста, например, если имя человека и другие данные не соответствуют ожиданиям.
Методы фальшивых и ложных объектов, которые возвращают данные якобы из хранилища данных или пользователя, могут помочь процессу тестирования, всегда возвращая одни и те же реалистичные данные, на которые могут положиться тесты. Их также можно перевести в заранее определенные режимы отказа, чтобы можно было разработать и надежно протестировать процедуры обработки ошибок. В режиме ошибки метод может вернуть недопустимый, неполный или нулевой ответ или может выдать исключение . Поддельные службы, отличные от хранилищ данных, также могут быть полезны в TDD: поддельная служба шифрования может фактически не шифровать передаваемые данные; поддельная служба случайных чисел всегда может возвращать 1. Поддельные или макетные реализации являются примерами внедрения зависимостей .
Тестовый дубль — это специфичная для теста возможность, которая заменяет системную возможность, обычно класс или функцию, от которой зависит проверяемое оборудование. Двойники тестов могут быть введены в систему в двух случаях: ссылка и выполнение. Подмена времени ссылки — это когда тестовый дубль компилируется в загрузочный модуль, который выполняется для проверки тестирования. Этот подход обычно используется при работе в среде, отличной от целевой среды, которая требует дублирования кода аппаратного уровня для компиляции. Альтернативой замене компоновщика является замена во время выполнения, при которой реальная функциональность заменяется во время выполнения тестового примера. Эта замена обычно выполняется путем переназначения известных указателей функций или замены объекта.
Тестовые дубли бывают разных типов и различной сложности:
- Манекен . Манекен — это простейшая форма тестового двойника. Это облегчает замену времени компоновщика, предоставляя при необходимости возвращаемое значение по умолчанию.
- Заглушка . Заглушка добавляет к манекену упрощенную логику, обеспечивая различные выходные данные.
- Шпион. Шпион фиксирует и предоставляет доступ к информации о параметрах и состоянии, публикуя методы доступа для проверки кода на наличие частной информации, что позволяет выполнять более сложную проверку состояния.
- Mock — макет определяется отдельным тестовым примером для проверки поведения конкретного теста, проверки значений параметров и последовательности вызовов.
- Симулятор. Симулятор представляет собой комплексный компонент, обеспечивающий более точное приближение к целевым возможностям (то, что удваивается). Симулятор обычно требует значительных дополнительных усилий по разработке. [ 9 ]
Следствием такого внедрения зависимостей является то, что реальная база данных или другой код внешнего доступа никогда не тестируется самим процессом TDD. Чтобы избежать ошибок, которые могут возникнуть из-за этого, необходимы другие тесты, которые создают экземпляры управляемого тестами кода с «реальными» реализациями интерфейсов, рассмотренных выше. Это интеграционные тесты , которые совершенно отличаются от модульных тестов TDD. Их меньше, и их нужно запускать реже, чем модульные тесты. Тем не менее, их можно реализовать с использованием той же среды тестирования.
Интеграционные тесты, которые изменяют любое постоянное хранилище или базу данных, всегда должны тщательно разрабатываться с учетом начального и конечного состояния файлов или базы данных, даже если какой-либо тест не пройден. Это часто достигается с помощью комбинации следующих методов:
- The
TearDown
метод, который является неотъемлемой частью многих платформ тестирования. try...catch...finally
Структуры обработки исключений, если они доступны.- Транзакции базы данных , где транзакция атомарно включает в себя, возможно, операции записи, чтения и соответствующего удаления.
- Создание «снимка» базы данных перед запуском каких-либо тестов и возврат к снимку после каждого запуска теста. Это можно автоматизировать с помощью такой платформы, как Ant или NAnt , или системы непрерывной интеграции, такой как CruiseControl .
- Инициализация базы данных до чистого состояния перед тестами, а не очистка после них. Это может быть актуально в тех случаях, когда очистка может затруднить диагностику сбоев теста из-за удаления конечного состояния базы данных до того, как можно будет выполнить детальную диагностику.
Держите устройство небольшим
[ редактировать ]В TDD единицу чаще всего определяют как класс или группу связанных функций, часто называемых модулем. Утверждается, что сохранение относительно небольших единиц дает важные преимущества, в том числе:
- Сокращение усилий по отладке. При обнаружении сбоев тестирования использование меньших модулей помогает отслеживать ошибки.
- Самодокументируемые тесты. Небольшие тестовые примеры легче читать и понимать. [ 9 ]
Передовые методы разработки через тестирование могут привести к разработке через приемочное тестирование (ATDD) и спецификации на примере , где критерии, указанные заказчиком, автоматизируются в приемочные тесты, которые затем запускают традиционный процесс разработки через модульное тестирование (UTDD). [ 16 ] Этот процесс гарантирует, что у клиента будет автоматизированный механизм принятия решения о том, соответствует ли программное обеспечение его требованиям. Благодаря ATDD перед командой разработчиков теперь стоит конкретная цель — приемочные тесты, — что позволяет им постоянно концентрироваться на том, чего клиент действительно хочет от каждой пользовательской истории.
Лучшие практики
[ редактировать ]Структура теста
[ редактировать ]Эффективная компоновка тестового примера гарантирует выполнение всех необходимых действий, улучшает читаемость тестового примера и упрощает поток выполнения. Последовательная структура помогает создать самодокументируемый тестовый пример. Обычно применяемая структура тестовых примеров включает (1) настройку, (2) выполнение, (3) проверку и (4) очистку.
- Настройка. Приведите тестируемый модуль (UUT) или всю тестовую систему в состояние, необходимое для запуска теста.
- Выполнение: запуск/управление проверяемым оборудованием для выполнения целевого поведения и захвата всех выходных данных, таких как возвращаемые значения и выходные параметры. Этот шаг обычно очень прост.
- Валидация: убедитесь, что результаты теста верны. Эти результаты могут включать явные выходные данные, полученные во время выполнения, или изменения состояния проверяемого оборудования.
- Очистка: восстановление проверяемого оборудования или всей тестовой системы до состояния, предшествующего тестированию. Это восстановление позволяет выполнить другой тест сразу после этого. В некоторых случаях, чтобы сохранить информацию для анализа возможного сбоя теста, очистку следует начинать непосредственно перед запуском установки теста. [ 9 ]
Индивидуальные лучшие практики
[ редактировать ]Некоторые передовые методы, которым может следовать человек, заключаются в том, чтобы разделить общую логику настройки и демонтажа на службы поддержки тестирования, используемые соответствующими тестовыми примерами, чтобы каждый тестовый оракул был сосредоточен только на результатах, необходимых для проверки его теста, и разрабатывать тесты, связанные со временем, чтобы обеспечить устойчивость к выполнению в операционных системах, не работающих в режиме реального времени. Обычная практика, предусматривающая запас в 5–10 процентов на позднее выполнение, снижает потенциальное количество ложноотрицательных результатов при выполнении теста. Также предлагается относиться к тестовому коду с тем же уважением, что и к рабочему коду. Тестовый код должен корректно работать как в положительных, так и в отрицательных случаях, работать долго, быть читабельным и удобным в сопровождении. Команды могут собраться вместе и просмотреть тесты и методы тестирования, чтобы поделиться эффективными методами и избавиться от вредных привычек. [ 17 ]
Практики, которых следует избегать, или «анти-паттерны»
[ редактировать ]- Наличие тестовых примеров зависит от состояния системы, манипулируемого ранее выполненными тестовыми примерами (т. е. вы всегда должны запускать модульный тест из известного и предварительно сконфигурированного состояния).
- Зависимости между тест-кейсами. Набор тестов, в котором тестовые примеры зависят друг от друга, является хрупким и сложным. Порядок исполнения не должен предполагаться. Базовый рефакторинг первоначальных тестовых примеров или структуры проверяемого оборудования вызывает спираль все более распространенных воздействий на связанные тесты.
- Взаимозависимые тесты. Взаимозависимые тесты могут вызывать каскад ложноотрицательных результатов. Сбой в первом тестовом примере нарушает последующий тестовый пример, даже если в проверяемом оборудовании нет фактической неисправности, что увеличивает усилия по анализу дефектов и отладке.
- Тестирование точного исполнения, поведения, времени или производительности.
- Создание «всезнающих оракулов». Оракул, который проверяет больше, чем необходимо, со временем становится более дорогим и хрупким. Эта очень распространенная ошибка опасна, поскольку она приводит к незаметному, но повсеместному снижению времени во всем сложном проекте. [ 17 ] [ нужны разъяснения ]
- Детали реализации тестирования.
- Медленное выполнение тестов.
Сравнение и разграничение
[ редактировать ]ТДД и АТДД
[ редактировать ]Разработка через тестирование связана с разработкой через приемочное тестирование (ATDD), но отличается от нее. [ 18 ] TDD — это прежде всего инструмент разработчика, помогающий создать хорошо написанную единицу кода (функцию, класс или модуль), которая правильно выполняет набор операций. ATDD — это инструмент связи между заказчиком, разработчиком и тестировщиком, позволяющий гарантировать четкое определение требований. TDD требует автоматизации тестирования. ATDD этого не делает, хотя автоматизация помогает при регрессионном тестировании. Тесты, используемые в TDD, часто могут быть получены из тестов ATDD, поскольку модули кода реализуют некоторую часть требований. Тесты ATDD должны быть доступны для чтения заказчиком. TDD-тесты не нужны.
ТДД и БДД
[ редактировать ]BDD ( разработка, основанная на поведении ) сочетает в себе практики TDD и ATDD. [ 19 ] Он включает в себя сначала практику написания тестов, но фокусируется на тестах, описывающих поведение, а не на тестах, проверяющих единицу реализации. Такие инструменты, как JBehave , Cucumber , Mspec и Specflow, предоставляют синтаксис, который позволяет владельцам продуктов, разработчикам и инженерам по тестированию совместно определять поведение, которое затем можно преобразовать в автоматизированные тесты.
Программное обеспечение для TDD
[ редактировать ]Существует множество фреймворков и инструментов тестирования, полезных в TDD.
фреймворки xUnit
[ редактировать ]компьютерного Разработчики могут использовать среды тестирования , обычно называемые xUnit (которые являются производными от SUnit, созданного в 1998 году), для создания и автоматического запуска тестовых примеров. Платформы xUnit предоставляют возможности проверки тестов в стиле утверждений и отчеты о результатах. Эти возможности имеют решающее значение для автоматизации, поскольку они переносят бремя проверки выполнения с независимой постобработки на операцию, которая включена в выполнение теста. Платформа выполнения, предоставляемая этими средами тестирования, позволяет автоматически выполнять все тестовые сценарии системы или различные их подмножества, а также другие функции. [ 20 ]
Результаты ТАР
[ редактировать ]Платформы тестирования могут принимать выходные данные модульного теста в независимом от языка протоколе Test Anything, созданном в 1987 году.
TDD для сложных систем
[ редактировать ]Применение TDD в больших и сложных системах требует модульной архитектуры, четко определенных компонентов с опубликованными интерфейсами и упорядоченного многоуровневого распределения систем с максимальной независимостью от платформы. Эти проверенные методы обеспечивают повышенную тестируемость и облегчают применение автоматизации сборки и тестирования. [ 9 ]
Проектирование для тестируемости
[ редактировать ]Сложные системы требуют архитектуры, отвечающей ряду требований. Ключевое подмножество этих требований включает поддержку полного и эффективного тестирования системы. Эффективная модульная конструкция позволяет получить компоненты, обладающие общими характеристиками, необходимыми для эффективного TDD.
- Высокая сплоченность гарантирует, что каждое подразделение предоставляет набор связанных возможностей, и упрощает поддержку тестов этих возможностей.
- Низкая связь позволяет эффективно тестировать каждое устройство изолированно.
- Опубликованные интерфейсы ограничивают доступ к компонентам и служат точками контакта для тестов, облегчая создание тестов и обеспечивая высочайшую точность между конфигурацией тестовых и производственных единиц.
Ключевым методом построения эффективной модульной архитектуры является моделирование сценариев, при котором строится набор диаграмм последовательности, каждая из которых ориентирована на один сценарий выполнения на уровне системы. Модель сценария представляет собой отличный инструмент для создания стратегии взаимодействия между компонентами в ответ на конкретный стимул. Каждая из этих моделей сценариев представляет собой богатый набор требований к сервисам или функциям, которые должен предоставлять компонент, а также определяет порядок, в котором эти компоненты и сервисы взаимодействуют друг с другом. Моделирование сценариев может значительно облегчить построение тестов TDD для сложной системы. [ 9 ]
Управление тестами для больших команд.
[ редактировать ]В более крупной системе влияние низкого качества компонентов усугубляется сложностью взаимодействия. Такое увеличение делает преимущества TDD еще быстрее в контексте более крупных проектов. Однако сложность общей совокупности тестов сама по себе может стать проблемой, сводя на нет потенциальные выгоды. Звучит просто, но ключевым начальным шагом является признание того, что тестовый код также является важным программным обеспечением и должен создаваться и поддерживаться с той же строгостью, что и рабочий код.
Создание и управление архитектурой тестового программного обеспечения в сложной системе так же важно, как и архитектура ядра продукта. Драйверы тестирования взаимодействуют с проверяемым оборудованием, двойниками тестов и платформой модульного тестирования. [ 9 ]
Преимущества и недостатки разработки через тестирование
[ редактировать ]Преимущества
[ редактировать ]Разработка через тестирование (TDD) — это подход к разработке программного обеспечения, при котором тесты пишутся до фактического кода. Он предлагает несколько преимуществ:
- Комплексное тестовое покрытие : TDD гарантирует, что весь новый код будет пройден хотя бы одним тестом, что приводит к созданию более надежного программного обеспечения.
- Повышенная уверенность в коде . Разработчики получают большую уверенность в надежности и функциональности кода.
- Хорошо документированный код . В результате этого процесса естественным образом создается хорошо документированный код, поскольку каждый тест проясняет назначение тестируемого кода.
- Ясность требований : TDD способствует четкому пониманию требований до начала кодирования.
- Облегчает непрерывную интеграцию : он хорошо интегрируется с процессами непрерывной интеграции, позволяя часто обновлять код и тестировать.
- Повышает производительность . Многие разработчики считают, что TDD повышает их производительность.
- Укрепляет ментальную модель кода : TDD помогает создать надежную ментальную модель структуры и поведения кода.
- Акцент на дизайне и функциональности : поощряет акцент на дизайне, интерфейсе и общей функциональности программы.
- Уменьшает потребность в отладке . Выявляя проблемы на ранних этапах процесса разработки, TDD снижает необходимость в дальнейшей отладке.
- Стабильность системы . Приложения, разработанные с использованием TDD, как правило, более стабильны и менее подвержены ошибкам. [ 21 ]
Недостатки
[ редактировать ]Однако TDD не лишен недостатков:
- Увеличение объема кода . Реализация TDD может привести к увеличению базы кода, поскольку тесты увеличивают общий объем написанного кода.
- Ложная безопасность от тестов . Большое количество пройденных тестов иногда может дать обманчивое ощущение безопасности относительно надежности кода. [ 22 ]
- Накладные расходы на обслуживание . Поддержание большого набора тестов может увеличить накладные расходы на процесс разработки.
- Отнимающие много времени процессы тестирования . Написание и поддержка тестов может занять много времени.
- Настройка среды тестирования : TDD требует настройки и поддержания подходящей среды тестирования.
- Кривая обучения : Чтобы овладеть практиками TDD, требуются время и усилия.
- Чрезмерное усложнение . Чрезмерное внимание к TDD может привести к тому, что код окажется более сложным, чем необходимо.
- Пренебрежение общим дизайном . Слишком узкое внимание к прохождению тестов иногда может привести к пренебрежению более широкой картиной проектирования программного обеспечения.
- Увеличение затрат . Дополнительное время и ресурсы, необходимые для TDD, могут привести к увеличению затрат на разработку.
Преимущества
[ редактировать ]Исследование 2005 года показало, что использование TDD означает написание большего количества тестов, и, в свою очередь, программисты, написавшие больше тестов, как правило, работают более продуктивно. [ 23 ] Гипотезы, касающиеся качества кода и более прямой корреляции между TDD и производительностью, оказались безрезультатными. [ 24 ]
Программисты, использующие чистый TDD в новых (« с нуля ») проектах, сообщали, что они лишь изредка ощущали необходимость вызывать отладчик . При использовании в сочетании с системой контроля версий в случае неожиданного сбоя тестов возврат кода к последней версии, прошедшей все тесты, часто может быть более продуктивным, чем отладка. [ 25 ]
Разработка через тестирование предлагает больше, чем просто проверку правильности, но также может способствовать разработке программы. [ 26 ] Сосредоточившись в первую очередь на тестовых примерах, необходимо представить, как функциональность используется клиентами (в первом случае — тестовые примеры). Итак, программист занимается интерфейсом до реализации. Это преимущество дополняет проектирование по контракту , поскольку оно подходит к коду через тестовые примеры, а не через математические утверждения или предубеждения.
Разработка через тестирование дает возможность делать небольшие шаги, когда это необходимо. Это позволяет программисту сосредоточиться на поставленной задаче, поскольку первая цель — пройти тест. Исключительные случаи и обработка ошибок изначально не рассматриваются, а тесты на создание этих посторонних обстоятельств реализуются отдельно. Таким образом, разработка через тестирование гарантирует, что весь написанный код будет пройден хотя бы одним тестом. Это дает команде программистов и последующим пользователям более высокий уровень доверия к коду.
Хотя это правда, что с TDD требуется больше кода, чем без TDD, из-за кода модульного тестирования, общее время реализации кода может быть короче, согласно модели Мюллера и Падберга. [ 27 ] Большое количество тестов помогает ограничить количество дефектов в коде. Ранний и частый характер тестирования помогает выявить дефекты на ранних этапах цикла разработки, не позволяя им стать эндемичными и дорогостоящими проблемами. Устранение дефектов на ранних стадиях процесса обычно позволяет избежать длительной и утомительной отладки на более поздних стадиях проекта.
TDD может привести к созданию более модульного, гибкого и расширяемого кода. Этот эффект часто возникает потому, что методология требует, чтобы разработчики думали о программном обеспечении как о небольших модулях, которые можно писать и тестировать независимо, а затем интегрировать вместе. Это приводит к меньшим, более целенаправленным классам, более слабой связи и более чистым интерфейсам. Использование шаблона проектирования макетного объекта также способствует общей модульности кода, поскольку этот шаблон требует, чтобы код был написан так, чтобы модули можно было легко переключать между макетными версиями для модульного тестирования и «настоящими» версиями для развертывания.
Поскольку для прохождения неудачного тестового примера не пишется больше кода, чем необходимо, автоматические тесты, как правило, охватывают каждый путь кода. Например, разработчик TDD может добавить else
перейти к существующему if
оператору, разработчику сначала придется написать неудачный тестовый пример, который мотивирует ветку. В результате автоматические тесты, полученные с помощью TDD, обычно бывают очень тщательными: они обнаруживают любые неожиданные изменения в поведении кода. Это позволяет обнаружить проблемы, которые могут возникнуть, когда изменение на более позднем этапе цикла разработки неожиданно изменяет другие функциональные возможности.
Madeyski [ 28 ] предоставил эмпирические доказательства (посредством серии лабораторных экспериментов с более чем 200 разработчиками) превосходства практики TDD над традиционным подходом Test-Last или подходом тестирования на корректность в отношении более низкой связи между объектами (CBO). Средний размер эффекта представляет собой средний (но близкий к большому) эффект на основе метаанализа проведенных экспериментов, что является важным выводом. Это предполагает лучшую модульность (т.е. более модульную конструкцию), более легкое повторное использование и тестирование разработанных программных продуктов благодаря практике программирования TDD. [ 28 ] Мадейски также измерил влияние практики TDD на модульные тесты, используя покрытие ветвей (BC) и индикатор оценки мутаций (MSI). [ 29 ] [ 30 ] [ 31 ] которые являются показателями тщательности и эффективности обнаружения ошибок модульных тестов соответственно. Влияние TDD на покрытие филиалов было средним и поэтому считается существенным. [ 28 ] Эти результаты впоследствии были подтверждены дальнейшими, меньшими экспериментальными оценками TDD. [ 32 ] [ 33 ] [ 34 ] [ 35 ]
Психологические преимущества для программиста
[ редактировать ]- Повышенная уверенность : TDD позволяет программистам с уверенностью вносить изменения или добавлять новые функции. Знание того, что код постоянно тестируется, снижает страх нарушить существующую функциональность. Эта система безопасности может стимулировать более инновационные и творческие подходы к решению проблем.
- Уменьшение страха перед изменениями, снижение стресса . При традиционной разработке изменение существующего кода может быть сложной задачей из-за риска появления ошибок. TDD с его обширным набором тестов уменьшает этот страх, поскольку тесты немедленно выявляют любые проблемы, вызванные изменениями. Знание того, что кодовая база имеет систему безопасности из тестов, может снизить стресс и беспокойство, связанные с программированием. Разработчики могут чувствовать себя более расслабленными и открытыми для экспериментов и рефакторинга.
- Улучшенная направленность : написание тестов сначала помогает программистам сконцентрироваться на требованиях и дизайне, прежде чем писать код. Такой фокус может привести к более четкому и целенаправленному кодированию, поскольку разработчик всегда осознает цель, которую он пытается достичь.
- Чувство достижения и удовлетворенность работой . Прохождение тестов может обеспечить быстрое и регулярное чувство выполненного долга, повышая моральный дух. Это может быть особенно мотивирующим в долгосрочных проектах, где конечная цель может показаться далекой. Сочетание всех этих факторов может привести к повышению удовлетворенности работой. Когда разработчики чувствуют себя уверенно, сосредоточенно и являются частью сплоченной команды, их общая удовлетворенность работой может значительно повыситься.
Ограничения
[ редактировать ]Этот раздел нуждается в дополнительных цитатах для проверки . ( Август 2013 г. ) |
Разработка через тестирование не обеспечивает достаточного тестирования в ситуациях, когда для определения успеха или неудачи требуются полные функциональные тесты, из-за широкого использования модульных тестов. [ 36 ] Примерами являются пользовательские интерфейсы , программы, работающие с базами данных , а также некоторые программы, зависящие от конкретных сети конфигураций . TDD призывает разработчиков помещать в такие модули минимальное количество кода и максимизировать логику, содержащуюся в тестируемом библиотечном коде, используя подделки и макеты для представления внешнего мира. [ 37 ]
Поддержка руководства очень важна. Если вся организация не поверит, что разработка через тестирование улучшит продукт, руководство может почувствовать, что время, потраченное на написание тестов, потрачено зря. [ 38 ]
Модульные тесты, созданные в среде разработки на основе тестирования, обычно создаются разработчиком, который пишет тестируемый код. Поэтому тесты могут иметь общие с кодом «слепые зоны»: если, например, разработчик не понимает, что определенные входные параметры необходимо проверять, скорее всего, ни тест, ни код не будут проверять эти параметры. Другой пример: если разработчик неправильно интерпретирует требования к разрабатываемому модулю, код и модульные тесты, которые он пишет, будут одинаково неправильными. Следовательно, тесты пройдут, давая ложное ощущение правильности.
Большое количество пройденных модульных тестов может создать ложное ощущение безопасности, в результате чего потребуется меньше дополнительных действий по тестированию программного обеспечения , таких как интеграционное тестирование и тестирование на соответствие .
Тесты становятся частью расходов на сопровождение проекта. Плохо написанные тесты, например те, которые включают жестко запрограммированные строки ошибок, сами по себе склонны к сбоям, и их дорого поддерживать. Особенно это касается хрупких тестов . [ 39 ] Существует риск того, что тесты, регулярно генерирующие ложные сбои, будут игнорироваться, так что при возникновении реального сбоя его можно будет не обнаружить. Можно писать тесты, требующие минимального и простого обслуживания, например, путем повторного использования строк ошибок, и это должно быть целью на этапе рефакторинга кода , описанном выше.
Написание и поддержка чрезмерного количества тестов требует времени. Кроме того, более гибкие модули (с ограниченным количеством тестов) могут принимать новые требования без необходимости изменения тестов. По этим причинам тестирование только в экстремальных условиях или на небольшой выборке данных может быть проще скорректировать, чем набор очень подробных тестов.
Уровень покрытия и детализации тестирования, достигнутый в ходе повторяющихся циклов TDD, нелегко воссоздать позднее. Таким образом, эти оригинальные или ранние тесты со временем становятся все более ценными. Тактика состоит в том, чтобы исправить это как можно раньше. Кроме того, если плохая архитектура, плохой дизайн или плохая стратегия тестирования приводят к позднему изменению, которое приводит к сбою десятков существующих тестов, важно, чтобы они были исправлены индивидуально. Простое их удаление, отключение или необдуманное изменение может привести к необнаружимым дырам в тестовом покрытии.
Конференция
[ редактировать ]Первая конференция TDD прошла в июле 2021 года. [ 40 ] Конференции записывались на YouTube [ 41 ]
См. также
[ редактировать ]- Приемочное тестирование
- Развитие, основанное на поведении
- Проектирование по договору
- Индуктивное программирование
- Интеграционное тестирование
- Список философий разработки программного обеспечения
- Список фреймворков модульного тестирования
- Макет объекта
- Программирование на примере
- Проверка работоспособности
- Код самотестирования
- Тестирование программного обеспечения
- Тестовый пример
- Предпосылка приоритета трансформации
- Модульное тестирование
- Непрерывная разработка через тестирование
Ссылки
[ редактировать ]- ^ Парса, Саид; Закери-Насрабади, Мортеза; Турхан, Бурак (1 января 2025 г.). «Разработка, основанная на тестируемости: повышение эффективности TDD» . Компьютерные стандарты и интерфейсы . 91 : 103877. doi : 10.1016/j.csi.2024.103877 . ISSN 0920-5489 .
- ^ Ли Коупленд (декабрь 2001 г.). «Экстремальное программирование» . Компьютерный мир. Архивировано из оригинала 5 июня 2011 года . Проверено 11 января 2011 г.
- ^ Jump up to: а б Ньюкирк Дж.В. и Воронцов А.А. Разработка через тестирование в Microsoft .NET , Microsoft Press, 2004.
- ^ Фезерс, М. Эффективная работа с устаревшим кодом, Прентис Холл, 2004 г.
- ^ Кент Бек (11 мая 2012 г.). «Почему Кент Бек говорит о «повторном открытии» разработки через тестирование?» . Проверено 1 декабря 2014 г.
- ^ Jump up to: а б с Бек, Кент (8 ноября 2002 г.). Разработка через тестирование на примере . Васим: Эддисон Уэсли. ISBN 978-0-321-14653-3 .
- ^ Лейборн, Э. (2013) Управление гибкой организацией: бережливый подход к управлению бизнесом . Лондон: Издательство IT Governance: 176–179.
- ^ Мохан, Гаятри. «Полное тестирование» . www. Thoughtworks.com . Проверено 7 сентября 2022 г.
- ^ Jump up to: а б с д и ж г «Информационный документ по эффективному TDD для сложных встраиваемых систем» (PDF) . Решения Pathfinder. Архивировано из оригинала (PDF) 16 марта 2016 г.
- ^ «Гибкая разработка через тестирование» . Проворный шерпа. 3 августа 2010 г. Архивировано из оригинала 23 июля 2012 г. Проверено 14 августа 2012 г.
- ^ Бертон, Росс (12 ноября 2003 г.). «Подрыв защиты доступа Java для модульного тестирования» . О'Рейли Медиа, Инк . Проверено 12 августа 2009 г.
- ^ ван Россум, Гвидо; Варшава, Барри (5 июля 2001 г.). «PEP 8 — Руководство по стилю для кода Python» . Фонд программного обеспечения Python . Проверено 6 мая 2012 г.
- ^ Ньюкирк, Джеймс (7 июня 2004 г.). «Тестирование частных методов/переменных-членов — стоит или не следует» . Корпорация Майкрософт . Проверено 12 августа 2009 г.
- ^ Столл, Тим (1 марта 2005 г.). «Как тестировать частные и защищенные методы в .NET» . КодПроект . Проверено 12 августа 2009 г.
- ^ Фаулер, Мартин (1999). Рефакторинг — улучшение дизайна существующего кода . Addison Wesley Longman, Inc. Бостон: ISBN 0-201-48567-2 .
- ^ Коскела, Л. «Test Driven: TDD и Acceptance TDD для разработчиков Java», Manning Publications, 2007
- ^ Jump up to: а б Разработка через тестирование (TDD) для внедрения сложных систем на YouTube от Pathfinder Solutions
- ^ Lean-Agile-разработка на основе приемочного тестирования: лучшее программное обеспечение благодаря сотрудничеству . Бостон: Эддисон Уэсли Профессионал. 2011. ISBN 978-0321714084 .
- ^ «БДД» . Архивировано из оригинала 8 мая 2015 г. Проверено 28 апреля 2015 г.
- ^ «Эффективное TDD для сложных встраиваемых систем» . Решения Pathfinder. Архивировано из оригинала 20 августа 2013 г. Проверено 27 ноября 2012 г.
- ^ Преимущества и недостатки разработки через тестирование - LASOFT
- ^ Парса, Саид; Закери-Насрабади, Мортеза; Турхан, Бурак (1 января 2025 г.). «Разработка, основанная на тестируемости: повышение эффективности TDD» . Компьютерные стандарты и интерфейсы . 91 : 103877. doi : 10.1016/j.csi.2024.103877 . ISSN 0920-5489 .
- ^ Эрдогмус, Хакан; Морисио, Торчиано. «Об эффективности подхода к программированию, основанного на тестировании» . Труды IEEE Transactions по разработке программного обеспечения, 31 (1). Январь 2005 г. (NRC 47445). Архивировано из оригинала 22 декабря 2014 г. Проверено 14 января 2008 г.
Мы обнаружили, что учащиеся, которые сначала тестировали, в среднем писали больше тестов, и, в свою очередь, учащиеся, которые писали больше тестов, имели тенденцию быть более продуктивными.
- ^ Проффитт, Джейкоб. «TDD доказал свою эффективность! Или нет?» . Архивировано из оригинала 6 февраля 2008 г. Проверено 21 февраля 2008 г.
Таким образом, отношение TDD к качеству в лучшем случае проблематично. Его связь с производительностью более интересна. Я надеюсь, что будет проведено дальнейшее исследование, потому что цифры производительности просто не очень хорошо для меня складываются. Существует неоспоримая корреляция между производительностью и количеством тестов, но эта корреляция на самом деле сильнее в группе без TDD (в которой был один выброс по сравнению с примерно половиной группы TDD, находящейся за пределами диапазона 95%).
- ^ Ллопис, Ноэль (20 февраля 2005 г.). «Шагая в Зазеркалье: Разработка игр через тестирование (Часть 1)» . Игры изнутри . Проверено 1 ноября 2007 г.
Сравнивая [TDD] с подходом к разработке, не основанным на тестировании, вы заменяете все мысленные проверки и пошаговые действия отладчика кодом, который проверяет, что ваша программа делает именно то, что вы задумали.
- ^ Майр, Хервиг (2005). Разработка инженерного программного обеспечения в проектных группах (2-е, новое изд.). Мюнхен: Fachbuchverl. Лейпциг в Карл-Ханзер-Верль. п. 239. ИСБН 978-3446400702 .
- ^ Мюллер, Матиас М.; Падберг, Фрэнк. «О возврате инвестиций в разработку через тестирование» (PDF) . Университет Карлсруэ, Германия. п. 6. S2CID 13905442 . Архивировано из оригинала (PDF) 8 ноября 2017 г. Проверено 14 июня 2012 г.
- ^ Jump up to: а б с Мадейски, Л. «Разработка через тестирование — эмпирическая оценка гибкой практики», Springer, 2010 г., ISBN 978-3-642-04287-4 , стр. 1-245. DOI: 978-3-642-04288-1.
- ^ Влияние программирования Test-First на охват ветвей и показатель мутационной оценки модульных тестов: эксперимент. Л. Мадейски, Информационные и программные технологии, 52(2): 169-184 (2010).
- ^ О влиянии парного программирования на тщательность и эффективность поиска ошибок модульных тестов, Л. Мадейски PROFES 2007: 207-221
- ^ Влияние парного программирования на тщательность и эффективность обнаружения ошибок в наборах модульных тестов. Л. Мадейски Программный процесс: совершенствование и практика 13 (3): 281-295 (2008).
- ^ М. Панчур и М. Цигларич, «Влияние разработки через тестирование на производительность, код и тесты: контролируемый эксперимент», Информационные и программные технологии, 2011, том. 53, нет. 6, стр. 557–573, DOI: 10.1016/j.infsof.2011.02.002.
- ^ Д. Фуччи, Х. Эрдогмус, Б. Турхан, М. Ойво и Н. Юристо, «Анализ процесса разработки через тестирование: действительно ли важно тестировать первым или тестировать последним?», IEEE Труды по программной инженерии, 2017, вып. 43, нет. Т. 7, стр. 597–614, DOI: 10.1109/TSE.2016.2616877.
- ^ А. Тосун, О. Диесте Тубио, Д. Фуччи, С. Вегас, Б. Турхан, Х. Эрдогмус, А. Сантос, М. Ойво, К. Торо, Дж. Ярвинен и Н. Юристо, «Индустрия эксперимент по влиянию разработки через тестирование на внешнее качество и производительность», Empirical Software Engineering, 2016, том. 22, стр. 1–43, DOI: 10.1007/s10664-016-9490-0.
- ^ Б. Папис, К. Гроховски, К. Субзда и К. Сийко, «Экспериментальная оценка разработки через тестирование с участием стажеров, работающих над реальным промышленным проектом», IEEE Transactions on Software Engineering, 2020, DOI: 10.1109/TSE.2020.3027522
- ^ «Проблемы с TDD» . Dalkescientific.com. 29 декабря 2009 г. Проверено 25 марта 2014 г.
- ^ Хантер, Эндрю (19 октября 2012 г.). «Злоупотребляют ли модульными тестами?» . Simple-talk.com . Проверено 25 марта 2014 г.
- ^ Логран, Стив (6 ноября 2006 г.). «Тестирование» (PDF) . Лаборатории HP . Проверено 12 августа 2009 г.
- ^ «Хрупкие испытания» .
- ^ Бунарджич, Алекс. «Первая международная конференция по разработке через тестирование (TDD)» . Конференция ТДД . Проверено 20 июля 2021 г.
- ^ Первая международная конференция TDD — суббота, 10 июля 2021 г. , заархивировано из оригинала 21 декабря 2021 г. , получено 20 июля 2021 г.
Внешние ссылки
[ редактировать ]- TestDrivenDevelopment на WikiWikiWeb
- Бертран Мейер (сентябрь 2004 г.). «Тест или спецификация? Тест и спецификация? Тест из спецификации!» . Архивировано из оригинала 9 февраля 2005 г.
- Командное тестирование Microsoft Visual Studio с использованием подхода TDD
- Напишите поддерживаемые модульные тесты, которые сэкономят вам время и слезы
- Повышение качества приложений с помощью разработки через тестирование (TDD)
- Конференция по разработке через тестирование