stringtranslate.com

Разработка через тестирование

Разработка через тестирование ( TDD ) — это способ написания кода , который включает в себя написание автоматизированного тестового случая на уровне модуля , который не проходит, затем написание кода, достаточного для прохождения теста, затем рефакторинг как тестового кода, так и производственного кода, а затем повторение с другим новым тестовым случаем.

Альтернативные подходы к написанию автоматизированных тестов — это написание всего производственного кода перед началом тестового кода или написание всего тестового кода перед началом производственного кода. При TDD оба пишутся вместе, что сокращает время отладки. [1]

TDD связана с концепциями программирования «сначала тест» экстремального программирования , начатыми в 1999 году [2] , но в последнее время вызвавшими более широкий интерес как таковые. [3]

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

История

Инженер-программист Кент Бек , которому приписывают разработку или «переоткрытие» [5] этой техники, заявил в 2003 году, что TDD поощряет простые проекты и внушает доверие. [6]

Первоначальное описание TDD было в древней книге о программировании. В ней говорилось, что вы берете входную ленту, вручную вводите на выходную ленту ожидаемый вами вывод, затем программируете до тех пор, пока фактическая выходная лента не будет соответствовать ожидаемому выводу. После того, как я написал первый фреймворк xUnit на Smalltalk, я вспомнил, что читал это, и попробовал. Для меня это было началом TDD. Когда я описывал TDD старшим программистам, я часто слышу: «Конечно. А как еще вы могли бы программировать?» Поэтому я называю свою роль «заново открывающим» TDD.

—  Кент Бек , «Почему Кент Бек говорит о «повторном открытии» разработки через тестирование? Какова история разработки через тестирование до повторного открытия Кентом Беком?» [7]

Цикл кодирования

Графическое представление жизненного цикла разработки через тестирование

Шаги TDD несколько различаются по количеству и описанию у разных авторов, но в целом они следующие. Они основаны на книге Test-Driven Development by Example [ 6] и статье Кента Бека Canon TDD [8] .

1. Перечислите сценарии для новой функции
Перечислите ожидаемые варианты в новом поведении. «Есть базовый случай, а затем что, если эта служба прервется, а затем что, если ключ еще не в базе данных, а затем…» Разработчик может узнать эти спецификации, спросив о вариантах использования и пользовательских историях . Ключевое преимущество TDD заключается в том, что он заставляет разработчика сосредоточиться на требованиях до написания кода. Это контрастирует с обычной практикой, когда модульные тесты пишутся только после кода.
2. Напишите тест для элемента из списка.
Напишите автоматизированный тест, который будет пройден, если вариант нового поведения будет соблюден.
3. Запустите все тесты. Новый тест должен провалиться — по ожидаемым причинам
Это показывает, что новый код действительно нужен для желаемой функции. Это подтверждает, что тестовая обвязка работает правильно. Это исключает возможность того, что новый тест неисправен и всегда будет проходить успешно.
4. Напишите простейший код, который пройдет новый тест.
Неэлегантный код и жесткое кодирование приемлемы. Код будет отточен на шаге 6. Код не должен быть добавлен за пределами проверенной функциональности.
5. Теперь все тесты должны пройти успешно.
Если какие-либо тесты не пройдены, исправьте не пройденные тесты, внеся минимальные изменения, пока все тесты не будут пройдены успешно.
6. При необходимости выполните рефакторинг, гарантируя, что все тесты продолжают проходить.
Код рефакторится для удобства чтения и обслуживания. В частности, жестко закодированные тестовые данные должны быть удалены из производственного кода. Запуск набора тестов после каждого рефакторинга гарантирует, что никакая существующая функциональность не будет нарушена. Примеры рефакторинга:
Повторить
Повторяйте процесс, начиная с шага 2, с каждым тестом из списка, пока все тесты не будут реализованы и пройдены.

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

При использовании внешних библиотек важно не писать тесты, которые настолько малы, что эффективно проверяют только саму библиотеку [3], если только нет оснований полагать, что библиотека содержит ошибки или недостаточно функциональна, чтобы удовлетворить все потребности разрабатываемого программного обеспечения.

Тестовая работа

TDD был принят за пределами разработки программного обеспечения, как в командах по продуктам, так и в командах по обслуживанию, как работа, основанная на тестировании . [9] Для успешного тестирования его необходимо проводить на микро- и макроуровнях. Каждый метод в классе, каждое значение входных данных, сообщение журнала и код ошибки, среди других точек данных, должны быть протестированы. [10] Подобно TDD, команды, не занимающиеся разработкой программного обеспечения, разрабатывают проверки контроля качества (QC) (обычно ручные тесты, а не автоматизированные тесты) для каждого аспекта работы до начала. Затем эти проверки QC используются для информирования о проекте и проверки связанных с ним результатов. Шесть шагов последовательности TDD применяются с небольшими семантическими изменениями:

  1. «Добавить проверку» заменяет «Добавить тест»
  2. «Выполнить все проверки» заменяет «Выполнить все тесты»
  3. «Выполняй работу» заменяет «Напиши код»
  4. «Выполнить все проверки» заменяет «Выполнить тесты»
  5. «Очистить работу» заменяет «Рефакторинг кода»
  6. "Повторить"

Стиль разработки

Существуют различные аспекты использования разработки через тестирование, например, принципы «упрости, глупый» ( KISS ) и « тебе это не понадобится » (YAGNI). Сосредоточившись на написании только необходимого для прохождения тестов кода, проекты часто могут быть чище и понятнее, чем это достигается другими методами. [6] В книге «Разработка через тестирование на примере » Кент Бек также предлагает принцип « Притворяйся, пока не сделаешь ».

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

Сначала напишите тесты: тесты должны быть написаны до функциональности, которая должна быть протестирована. Утверждается, что это имеет много преимуществ. Это помогает гарантировать, что приложение написано для возможности тестирования, так как разработчики должны продумать, как протестировать приложение с самого начала, а не добавлять его позже. Это также гарантирует, что тесты для каждой функции будут написаны. Кроме того, написание тестов в первую очередь приводит к более глубокому и раннему пониманию требований к продукту, обеспечивает эффективность тестового кода и поддерживает постоянную фокусировку на качестве программного обеспечения . [11] При написании кода, ориентированного на функции, разработчики и организации склонны подталкивать разработчика к следующей функции, даже полностью игнорируя тестирование. Первый тест TDD может даже не скомпилироваться сначала, потому что требуемые ему классы и методы могут еще не существовать. Тем не менее, этот первый тест функционирует как начало исполняемой спецификации. [12]

Каждый тестовый случай изначально терпит неудачу: это гарантирует, что тест действительно работает и может обнаружить ошибку. Как только это будет показано, может быть реализована базовая функциональность. Это привело к «мантре разработки через тестирование», которая звучит как «красный/зеленый/рефакторинг», где красный означает провал , а зеленый означает прохождение . Разработка через тестирование постоянно повторяет шаги добавления тестовых случаев, которые терпят неудачу, их прохождения и рефакторинга. Получение ожидаемых результатов теста на каждом этапе укрепляет ментальную модель кода разработчика, повышает уверенность и увеличивает производительность.

Видимость кода

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

В объектно-ориентированном проектировании это все еще не обеспечивает доступ к закрытым данным и методам. Поэтому для модульных тестов может потребоваться дополнительная работа. В Java и других языках разработчик может использовать рефлексию для доступа к закрытым полям и методам. [13] В качестве альтернативы внутренний класс может использоваться для хранения модульных тестов, чтобы они имели видимость членов и атрибутов включающего класса. В .NET Framework и некоторых других языках программирования частичные классы могут использоваться для предоставления закрытых методов и данных для доступа тестов.

Важно, чтобы такие тестовые хаки не оставались в производственном коде. В C и других языках директивы компилятора, такие как , #if DEBUG ... #endifмогут быть размещены вокруг таких дополнительных классов и, конечно, всего другого кода, связанного с тестированием, чтобы предотвратить их компиляцию в выпущенный код. Это означает, что выпущенный код не совсем такой же, как тот, который был протестирован модулем. Регулярное выполнение меньшего количества, но более полных, сквозных интеграционных тестов в окончательной сборке выпуска может гарантировать (помимо прочего), что не существует производственного кода, который тонко зависит от аспектов тестового оборудования.

Среди практиков TDD ведутся споры, задокументированные в их блогах и других работах, о том, разумно ли вообще тестировать частные методы и данные. Некоторые утверждают, что частные члены — это просто деталь реализации, которая может измениться, и им должно быть разрешено делать это без нарушения ряда тестов. Таким образом, должно быть достаточно протестировать любой класс через его открытый интерфейс или через интерфейс его подкласса, который некоторые языки называют «защищенным» интерфейсом. [14] Другие говорят, что критически важные аспекты функциональности могут быть реализованы в частных методах, и их прямое тестирование дает преимущество меньших и более прямых модульных тестов. [15] [16]

Подделки, макеты и интеграционные тесты

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

Когда разрабатываемый код опирается на базу данных, веб-сервис или любой другой внешний процесс или сервис, обеспечение разделения с возможностью модульного тестирования также является возможностью и движущей силой для разработки более модульного, более тестируемого и более повторно используемого кода. [17] Необходимо два шага:

  1. Всякий раз, когда в окончательном проекте требуется внешний доступ, следует определить интерфейс , описывающий доступный доступ. См. принцип инверсии зависимости для обсуждения преимуществ этого независимо от TDD.
  2. Интерфейс должен быть реализован двумя способами, один из которых действительно обращается к внешнему процессу, а другой — поддельный или фиктивный . Поддельным объектам нужно сделать немного больше, чем добавить сообщение, например, «Объект Person сохранен» в журнал трассировки , против которого можно запустить тестовое утверждение для проверки правильного поведения. Поддельные объекты отличаются тем, что они сами содержат тестовые утверждения , которые могут привести к сбою теста, например, если имя человека и другие данные не соответствуют ожидаемым.

Методы поддельных и фиктивных объектов, которые возвращают данные, якобы из хранилища данных или пользователя, могут помочь в процессе тестирования, всегда возвращая одни и те же реалистичные данные, на которые могут полагаться тесты. Их также можно установить в предопределенные режимы сбоя, чтобы можно было разработать и надежно протестировать процедуры обработки ошибок. В режиме сбоя метод может возвращать недействительный, неполный или нулевой ответ или может выдавать исключение . Поддельные службы, отличные от хранилищ данных, также могут быть полезны в TDD: поддельная служба шифрования может на самом деле не шифровать передаваемые данные; поддельная служба случайных чисел может всегда возвращать 1. Поддельные или фиктивные реализации являются примерами внедрения зависимостей .

Тестовый двойник — это тестовая специфичная возможность, которая заменяет системную возможность, обычно класс или функцию, от которой зависит UUT. Тестовые двойники могут быть введены в систему в двух случаях: при компоновке и выполнении. Замена во время компоновки происходит, когда тестовый двойник компилируется в загрузочный модуль, который выполняется для проверки тестирования. Этот подход обычно используется при запуске в среде, отличной от целевой среды, которая требует двойников для кода аппаратного уровня для компиляции. Альтернативой замене компоновщика является замена во время выполнения, при которой реальная функциональность заменяется во время выполнения тестового случая. Эта замена обычно выполняется путем переназначения известных указателей на функции или замены объекта.

Тестовые двойники бывают разных типов и разной сложности:

Следствием такого внедрения зависимостей является то, что фактическая база данных или другой код внешнего доступа никогда не тестируется самим процессом TDD. Чтобы избежать ошибок, которые могут возникнуть из-за этого, необходимы другие тесты, которые создают экземпляр тестового кода с «реальными» реализациями интерфейсов, обсуждаемых выше. Это интеграционные тесты , и они довольно сильно отличаются от модульных тестов TDD. Их меньше, и они должны запускаться реже, чем модульные тесты. Тем не менее, их можно реализовать с использованием той же самой среды тестирования.

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

Сохраняйте небольшой размер подразделения

Для TDD блок чаще всего определяется как класс или группа связанных функций, часто называемых модулем. Утверждается, что сохранение относительно небольших блоков обеспечивает критические преимущества, в том числе:

Расширенные практики разработки через тестирование могут привести к разработке через приемочные тесты (ATDD) и спецификации на примере , где критерии, указанные заказчиком, автоматизированы в приемочные тесты, которые затем управляют традиционным процессом разработки через модульное тестирование (UTDD). [18] Этот процесс гарантирует заказчику автоматизированный механизм для принятия решения о том, соответствует ли программное обеспечение его требованиям. С ATDD у команды разработчиков теперь есть конкретная цель для удовлетворения — приемочные тесты — которые постоянно держат их в фокусе на том, что заказчик действительно хочет от каждой пользовательской истории.

Лучшие практики

Структура теста

Эффективная компоновка тестового случая гарантирует выполнение всех требуемых действий, улучшает читаемость тестового случая и сглаживает поток выполнения. Последовательная структура помогает в построении самодокументирующего тестового случая. Обычно применяемая структура для тестовых случаев имеет (1) настройку, (2) выполнение, (3) проверку и (4) очистку.

Индивидуальные лучшие практики

Некоторые лучшие практики, которым может следовать отдельный человек, заключаются в том, чтобы разделить общую логику настройки и демонтажа на службы поддержки тестирования, используемые соответствующими тестовыми случаями, чтобы каждый тестовый оракул был сосредоточен только на результатах, необходимых для проверки его теста, и разрабатывать тесты, связанные со временем, чтобы обеспечить допуск для выполнения в операционных системах не в реальном времени. Распространенная практика предоставления 5-10-процентного запаса для позднего выполнения снижает потенциальное количество ложных отрицательных результатов при выполнении теста. Также предлагается относиться к тестовому коду с таким же уважением, как и к производственному коду. Тестовый код должен работать правильно как для положительных, так и для отрицательных случаев, служить долго, быть читаемым и поддерживаемым. Команды могут собираться вместе и просматривать тесты и методы тестирования, чтобы делиться эффективными методами и выявлять плохие привычки. [19]

Практики, которых следует избегать, или «анти-шаблоны»

Сравнение и разграничение

TDD и ATDD

Разработка через тестирование связана с разработкой через приемочное тестирование (ATDD), но отличается от нее. [20] TDD — это в первую очередь инструмент разработчика, помогающий создавать хорошо написанную единицу кода (функцию, класс или модуль), которая правильно выполняет набор операций. ATDD — это инструмент общения между заказчиком, разработчиком и тестировщиком для обеспечения четкого определения требований. TDD требует автоматизации тестирования. ATDD этого не делает, хотя автоматизация помогает с регрессионным тестированием. Тесты, используемые в TDD, часто могут быть получены из тестов ATDD, поскольку единицы кода реализуют некоторую часть требования. Тесты ATDD должны быть читаемыми заказчиком. Тесты TDD не должны быть такими.

TDD и BDD

BDD ( разработка на основе поведения ) объединяет практики из TDD и из ATDD. [21] Он включает практику написания тестов в первую очередь, но фокусируется на тестах, которые описывают поведение, а не на тестах, которые проверяют единицу реализации. Такие инструменты, как JBehave, Cucumber , Mspec и Specflow, предоставляют синтаксисы, которые позволяют владельцам продукта, разработчикам и инженерам по тестированию совместно определять поведение, которое затем может быть переведено в автоматизированные тесты.

Программное обеспечение для TDD

Существует множество фреймворков и инструментов тестирования, полезных в TDD.

xUnit-фреймворки

Разработчики могут использовать автоматизированные фреймворки тестирования , обычно называемые xUnit (которые являются производными от SUnit, созданного в 1998 году), для создания и автоматического запуска тестовых случаев. Фреймворки xUnit предоставляют возможности проверки тестов в стиле утверждений и отчеты о результатах. Эти возможности имеют решающее значение для автоматизации, поскольку они переносят бремя проверки выполнения с независимой постобработки на ту, которая включена в выполнение теста. Фреймворк выполнения, предоставляемый этими тестовыми фреймворками, позволяет автоматически выполнять все системные тестовые случаи или различные подмножества вместе с другими функциями. [22]

Результаты ТАП

Тестовые фреймворки могут принимать выходные данные модульного теста в не зависящем от языка протоколе Test Anything, созданном в 1987 году.

TDD для сложных систем

Применение TDD на больших, сложных системах требует модульной архитектуры, четко определенных компонентов с опубликованными интерфейсами и дисциплинированной системной иерархии с максимизацией независимости платформы. Эти проверенные практики обеспечивают повышенную тестируемость и облегчают применение автоматизации сборки и тестирования. [11]

Проектирование для обеспечения тестируемости

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

Ключевым методом построения эффективной модульной архитектуры является моделирование сценариев, при котором создается набор диаграмм последовательности, каждая из которых фокусируется на одном сценарии выполнения на уровне системы. Модель сценария предоставляет превосходное средство для создания стратегии взаимодействия между компонентами в ответ на определенный стимул. Каждая из этих моделей сценария служит богатым набором требований к услугам или функциям, которые должен предоставлять компонент, а также диктует порядок, в котором эти компоненты и услуги взаимодействуют друг с другом. Моделирование сценариев может значительно облегчить построение тестов TDD для сложной системы. [11]

Управление тестами для больших команд

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

Создание и управление архитектурой тестового ПО в рамках сложной системы так же важно, как и архитектура основного продукта. Тестовые драйверы взаимодействуют с UUT, тестовыми двойниками и фреймворком модульного тестирования. [11]

Преимущества и недостатки разработки через тестирование

Преимущества

Разработка через тестирование (TDD) — это подход к разработке программного обеспечения, при котором тесты пишутся до написания фактического кода. Он предлагает несколько преимуществ:

  1. Всестороннее покрытие тестами : TDD гарантирует, что весь новый код покрыт как минимум одним тестом, что приводит к созданию более надежного программного обеспечения.
  2. Повышение уверенности в коде : разработчики обретают большую уверенность в надежности и функциональности кода.
  3. Хорошо документированный код : процесс естественным образом приводит к хорошо документированному коду, поскольку каждый тест проясняет цель проверяемого кода.
  4. Ясность требований : TDD способствует четкому пониманию требований до начала кодирования.
  5. Облегчает непрерывную интеграцию : хорошо интегрируется с процессами непрерывной интеграции, позволяя часто обновлять код и проводить тестирование.
  6. Повышение производительности : многие разработчики отмечают, что TDD повышает их производительность.
  7. Укрепляет ментальную модель кода : TDD помогает создать сильную ментальную модель структуры и поведения кода.
  8. Акцент на дизайне и функциональности : поощряет сосредоточение внимания на дизайне, интерфейсе и общей функциональности программы.
  9. Снижает необходимость отладки : выявляя проблемы на ранних этапах процесса разработки, TDD снижает необходимость в обширной отладке в дальнейшем.
  10. Стабильность системы : приложения, разработанные с использованием TDD, как правило, более стабильны и менее подвержены ошибкам. [23]

Недостатки

Однако TDD не лишена недостатков:

  1. Увеличение объема кода : внедрение TDD может привести к увеличению объема кодовой базы, поскольку тесты увеличивают общий объем написанного кода.
  2. Ложная безопасность от тестов : Большое количество пройденных тестов иногда может дать обманчивое чувство безопасности относительно надежности кода. [24]
  3. Накладные расходы на обслуживание : Поддержание большого набора тестов может привести к увеличению накладных расходов на процесс разработки.
  4. Процессы тестирования, требующие много времени : написание и поддержка тестов может занять много времени.
  5. Настройка среды тестирования : TDD требует настройки и поддержания подходящей среды тестирования.
  6. Кривая обучения : чтобы овладеть методами TDD, требуются время и усилия.
  7. Излишнее усложнение : чрезмерный акцент на TDD может привести к тому, что код окажется сложнее, чем необходимо.
  8. Пренебрежение общим дизайном : слишком узкая концентрация на прохождении тестов иногда может привести к пренебрежению более общей картиной проектирования программного обеспечения.
  9. Увеличение затрат : дополнительное время и ресурсы, необходимые для TDD, могут привести к увеличению затрат на разработку.

Преимущества

Исследование 2005 года показало, что использование TDD означало написание большего количества тестов, и, в свою очередь, программисты, которые писали больше тестов, как правило, были более продуктивны. [25] Гипотезы, касающиеся качества кода и более прямой корреляции между TDD и производительностью, оказались неубедительными. [26]

Программисты, использующие чистый TDD на новых (« greenfield ») проектах, сообщали, что они лишь изредка испытывали необходимость в вызове отладчика . При использовании в сочетании с системой контроля версий , когда тесты неожиданно терпят неудачу, возврат кода к последней версии, которая прошла все тесты, часто может быть более продуктивным, чем отладка. [27]

Разработка через тестирование предлагает больше, чем просто проверку правильности, но также может управлять дизайном программы. [28] Сосредоточившись сначала на тестовых случаях, нужно представить, как функциональность используется клиентами (в первом случае, тестовые случаи). Таким образом, программист озабочен интерфейсом до реализации. Это преимущество является дополнительным к проектированию по контракту , поскольку оно подходит к коду через тестовые случаи, а не через математические утверждения или предубеждения.

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

Хотя верно, что при TDD требуется больше кода, чем без TDD из-за кода модульного теста, общее время реализации кода может быть короче на основе модели Мюллера и Падберга. [29] Большое количество тестов помогает ограничить количество дефектов в коде. Ранний и частый характер тестирования помогает обнаружить дефекты на ранних этапах цикла разработки, не давая им стать эндемичными и дорогостоящими проблемами. Устранение дефектов на ранних этапах процесса обычно позволяет избежать длительной и утомительной отладки на более поздних этапах проекта.

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

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

Мадейски [30] предоставил эмпирические доказательства (через серию лабораторных экспериментов с более чем 200 разработчиками) относительно превосходства практики TDD над традиционным подходом Test-Last или подходом тестирования на корректность в отношении более низкой связи между объектами (CBO). Средний размер эффекта представляет собой средний (но близкий к большому) эффект на основе метаанализа проведенных экспериментов, что является существенным выводом. Он предполагает лучшую модуляризацию (т. е. более модульную конструкцию), более легкое повторное использование и тестирование разработанных программных продуктов благодаря практике программирования TDD. [30] Мадейски также измерил эффект практики TDD на модульные тесты с использованием покрытия ветвей (BC) и индикатора оценки мутации (MSI), [31] [32] [33], которые являются индикаторами тщательности и эффективности обнаружения неисправностей модульных тестов соответственно. Размер эффекта TDD на покрытие ветвей был средним по размеру и, следовательно, считается существенным эффектом. [30] Эти выводы были впоследствии подтверждены дальнейшими, меньшими экспериментальными оценками TDD. [34] [35] [36] [37]

Психологические преимущества для программиста

  1. Повышение уверенности : TDD позволяет программистам с уверенностью вносить изменения или добавлять новые функции. Знание того, что код постоянно тестируется, снижает страх сломать существующую функциональность. Эта страховочная сетка может поощрять более инновационные и креативные подходы к решению проблем.
  2. Уменьшение страха перед изменениями, снижение стресса : в традиционной разработке изменение существующего кода может быть пугающим из-за риска внесения ошибок. TDD с его всеобъемлющим набором тестов уменьшает этот страх, поскольку тесты немедленно выявят любые проблемы, вызванные изменениями. Знание того, что кодовая база имеет защитную сетку тестов, может снизить стресс и беспокойство, связанные с программированием. Разработчики могут чувствовать себя более расслабленными и открытыми для экспериментов и рефакторинга.
  3. Улучшенный фокус : написание тестов в первую очередь помогает программистам сосредоточиться на требованиях и дизайне, прежде чем писать код. Такой фокус может привести к более ясному, более целенаправленному кодированию, поскольку разработчик всегда осознает цель, которую он пытается достичь.
  4. Чувство достижения и удовлетворенность работой : прохождение тестов может обеспечить быстрое, регулярное чувство достижения, повышая моральный дух. Это может быть особенно мотивирующим в долгосрочных проектах, где конечная цель может казаться далекой. Сочетание всех этих факторов может привести к повышению удовлетворенности работой. Когда разработчики чувствуют себя уверенными, сосредоточенными и частью совместной команды, их общая удовлетворенность работой может значительно повыситься.

Ограничения

Разработка через тестирование не выполняет достаточного тестирования в ситуациях, когда для определения успеха или неудачи требуются полные функциональные тесты, из-за широкого использования модульных тестов. [38] Примерами этого являются пользовательские интерфейсы , программы, работающие с базами данных , и некоторые, зависящие от определенных сетевых конфигураций. TDD призывает разработчиков помещать минимальный объем кода в такие модули и максимизировать логику, которая находится в тестируемом библиотечном коде, используя подделки и макеты для представления внешнего мира. [39]

Поддержка руководства имеет важное значение. Если вся организация не верит, что разработка через тестирование улучшит продукт, руководство может посчитать, что время, потраченное на написание тестов, тратится впустую. [40]

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

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

Тесты становятся частью накладных расходов на обслуживание проекта. Плохо написанные тесты, например, те, которые включают жестко закодированные строки ошибок, сами по себе склонны к сбоям, и их поддержка обходится дорого. Это особенно касается хрупких тестов. [41] Существует риск того, что тесты, которые регулярно генерируют ложные сбои, будут проигнорированы, так что когда произойдет реальный сбой, он может быть не обнаружен. Можно написать тесты для простого и легкого обслуживания, например, путем повторного использования строк ошибок, и это должно быть целью на этапе рефакторинга кода , описанном выше.

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

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

Конференция

Первая конференция TDD прошла в июле 2021 года. [42] Конференции были записаны на YouTube [43]

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

Ссылки

  1. ^ Парса, Саид; Закери-Насрабади, Мортеза; Турхан, Бурак (01.01.2025). «Разработка, основанная на тестируемости: улучшение эффективности TDD». Компьютерные стандарты и интерфейсы . 91 : 103877. doi : 10.1016/j.csi.2024.103877. ISSN  0920-5489.
  2. Ли Коупленд (декабрь 2001 г.). «Экстремальное программирование». Computerworld. Архивировано из оригинала 5 июня 2011 г. Получено 11 января 2011 г.
  3. ^ ab Ньюкирк, Дж. В. и Воронцов, А. А. Разработка через тестирование в Microsoft .NET , Microsoft Press, 2004.
  4. ^ Фезерс, М. Эффективная работа с устаревшим кодом, Prentice Hall, 2004
  5. ^ Кент Бек (11 мая 2012 г.). «Почему Кент Бек ссылается на «повторное открытие» разработки через тестирование?» . Получено 1 декабря 2014 г. .
  6. ^ abc Бек, Кент (2002-11-08). Разработка через тестирование на примере . Vaseem: Addison Wesley. ISBN 978-0-321-14653-3.
  7. ^ Кент Бек (11 мая 2012 г.). «Почему Кент Бек ссылается на «повторное открытие» разработки через тестирование?» . Получено 1 декабря 2014 г. .
  8. ^ Бек, Кент (2023-12-11). "Canon TDD". Разработка программного обеспечения: сначала Tidy? . Получено 2024-10-22 .
  9. ^ Лейборн, Э. (2013) Руководство гибкой организацией: бережливый подход к управлению бизнесом . Лондон: Издательство IT Governance: 176-179.
  10. ^ Мохан, Гаятри. «Полное тестирование стека». www.thoughtworks.com . Получено 07.09.2022 .
  11. ^ abcdefg "Effective TDD for Complex Embedded Systems Whitepaper" (PDF) . Pathfinder Solutions. Архивировано из оригинала (PDF) 2016-03-16.
  12. ^ "Agile Test Driven Development". Agile Sherpa. 2010-08-03. Архивировано из оригинала 2012-07-23 . Получено 2012-08-14 .
  13. ^ Бертон, Росс (12.11.2003). «Подрыв защиты доступа Java для модульного тестирования». O'Reilly Media, Inc. Получено 12.08.2009 .
  14. ^ ван Россум, Гвидо; Варшава, Барри (5 июля 2001 г.). "PEP 8 — Руководство по стилю кода Python". Python Software Foundation . Получено 6 мая 2012 г.
  15. ^ Ньюкирк, Джеймс (7 июня 2004 г.). «Тестирование частных методов/переменных-членов — стоит ли это делать или нет». Корпорация Microsoft . Получено 12 августа 2009 г.
  16. ^ Stall, Tim (1 марта 2005 г.). "Как тестировать закрытые и защищенные методы в .NET". CodeProject . Получено 12 августа 2009 г.
  17. ^ Фаулер, Мартин (1999). Рефакторинг — Улучшение дизайна существующего кода. Бостон: Addison Wesley Longman, Inc. ISBN 0-201-48567-2.
  18. ^ Коскела, Л. «Test Driven: TDD и приемка TDD для разработчиков Java», Manning Publications, 2007
  19. ^ ab Test-Driven Development (TDD) для сложных систем Введение на YouTube от Pathfinder Solutions
  20. ^ Разработка Lean-Agile через приемочные испытания: лучшее программное обеспечение через сотрудничество . Бостон: Addison Wesley Professional. 2011. ISBN 978-0321714084.
  21. ^ "BDD". Архивировано из оригинала 2015-05-08 . Получено 2015-04-28 .
  22. ^ "Effective TDD for Complex, Embedded Systems Whitepaper". Pathfinder Solutions. Архивировано из оригинала 20-08-2013 . Получено 27-11-2012 .
  23. ^ Преимущества и недостатки разработки через тестирование - LASOFT
  24. ^ Парса, Саид; Закери-Насрабади, Мортеза; Турхан, Бурак (01.01.2025). «Разработка, основанная на тестируемости: улучшение эффективности TDD». Компьютерные стандарты и интерфейсы . 91 : 103877. doi : 10.1016/j.csi.2024.103877. ISSN  0920-5489.
  25. ^ Эрдогмус, Хакан; Морисио, Торкиано. «Об эффективности подхода «тест-сначала» к программированию». Труды IEEE Transactions on Software Engineering, 31(1). Январь 2005 г. (NRC 47445). Архивировано из оригинала 2014-12-22 . Получено 2008-01-14 . Мы обнаружили, что студенты, которые в среднем писали больше тестов, и, в свою очередь, студенты, которые писали больше тестов, как правило, были более продуктивны.
  26. ^ Проффитт, Джейкоб. "TDD Proven Effective! Или это так?". Архивировано из оригинала 2008-02-06 . Получено 2008-02-21 . Так что связь TDD с качеством в лучшем случае проблематична. Ее связь с производительностью более интересна. Я надеюсь, что будет последующее исследование, потому что цифры производительности просто не очень хорошо складываются для меня. Существует неоспоримая корреляция между производительностью и количеством тестов, но эта корреляция на самом деле сильнее в группе без TDD (где был один выброс по сравнению с примерно половиной группы TDD, находящейся за пределами диапазона 95%).
  27. ^ Llopis, Noel (20 февраля 2005 г.). "Stepping Through the Looking Glass: Test-Driven Game Development (Part 1)". Games from Within . Получено 01.11.2007 . Сравнивая [TDD] с подходом к разработке без тестирования, вы заменяете все мысленные проверки и пошаговое выполнение отладчика кодом, который проверяет, что ваша программа делает именно то, что вы от нее хотели.
  28. ^ Майр, Хервиг (2005). Projekt Engineering Ingenieurmässige Softwareentwicklung in Projektgruppen (2., neu Bearb. Aufl. Ed.). Мюнхен: Fachbuchverl. Лейпциг им Карл-Хансер-Верль. п. 239. ИСБН 978-3446400702.
  29. ^ Мюллер, Маттиас М.; Падберг, Франк. «О возврате инвестиций в разработку через тестирование» (PDF) . Университет Карлсруэ, Германия. стр. 6. S2CID  13905442. Архивировано из оригинала (PDF) 2017-11-08 . Получено 2012-06-14 .
  30. ^ abc Madeyski, L. "Test-Driven Development - An Empirical Evaluation of Agile Practice", Springer, 2010, ISBN 978-3-642-04287-4 , стр. 1-245. DOI: 978-3-642-04288-1 
  31. ^ Влияние программирования Test-First на покрытие ветвей и показатель оценки мутации модульных тестов: эксперимент. Л. Мадейски Information & Software Technology 52(2): 169-184 (2010)
  32. ^ О влиянии парного программирования на тщательность и эффективность поиска неисправностей модульных тестов Л. Мадейски PROFES 2007: 207-221
  33. ^ Влияние парного программирования на тщательность и эффективность обнаружения неисправностей наборов модульных тестов. Л. Мадейски Программный процесс: улучшение и практика 13(3): 281-295 (2008)
  34. ^ М. Панчур и М. Цигларич, «Влияние разработки через тестирование на производительность, код и тесты: контролируемый эксперимент», Информационные и программные технологии, 2011, т. 53, № 6, стр. 557–573, DOI: 10.1016/j.infsof.2011.02.002
  35. ^ Д. Фуччи, Х. Эрдогмус, Б. Турхан, М. Ойво и Н. Юристо, «Анализ процесса разработки через тестирование: действительно ли важно тестировать в первую очередь или в последнюю?», IEEE Transactions on Software Engineering, 2017, т. 43, № 7, стр. 597–614, DOI: 10.1109/TSE.2016.2616877
  36. ^ А. Тосун, О. Диесте Тубио, Д. Фуччи, С. Вегас, Б. Турхан, Х. Эрдогмус, А. Сантос, М. Ойво, К. Торо, Дж. Ярвинен и Н. Юристо, «Отраслевой эксперимент по влиянию разработки через тестирование на внешнее качество и производительность», Empirical Software Engineering, 2016, т. 22, стр. 1–43, DOI: 10.1007/s10664-016-9490-0
  37. ^ Б. Папис, К. Гроховски, К. Субзда и К. Сийко, «Экспериментальная оценка разработки через тестирование с участием стажеров, работающих над реальным промышленным проектом», IEEE Transactions on Software Engineering, 2020, DOI: 10.1109/TSE.2020.3027522
  38. ^ "Проблемы с TDD". Dalkescientific.com. 2009-12-29 . Получено 2014-03-25 .
  39. ^ Хантер, Эндрю (2012-10-19). "Are Is Unit Tests Overuse?". Simple-talk.com . Получено 2014-03-25 .
  40. ^ Логран, Стив (6 ноября 2006 г.). "Тестирование" (PDF) . HP Laboratories . Получено 2009-08-12 .
  41. ^ «Хрупкие тесты».
  42. ^ Бунарджич, Алекс. "Первая международная конференция по разработке через тестирование (TDD)". Конференция TDD . Получено 20 июля 2021 г.
  43. ^ Первая международная конференция TDD — суббота, 10 июля 2021 г., 10 июля 2021 г., архивировано из оригинала 21.12.2021 г. , извлечено 20.07.2021 г.

Внешние ссылки