TDD существует с 1999 года и является методом разработки программного обеспечения, основанном на тестировании. В 2003 году американский разработчик Кент Бек «заново открыл» TDD и применил его как способ создания простых конструкций и внушающих доверие. На сегодняшний день, TDD – это процесс разработки программного обеспечения, использующий очень короткий цикл обратной связи, при котором разработчики:

  1. Создают тест, который не может быть пройден.
  2. Пишут рабочий код, который пройдёт тест.
  3. Делают рефакторинг кода, написанного на втором шаге, используя созданный тест доводят код до совершенства.

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

Оглавление

Развитие TDD
Что же такое Test Driven Development (TDD)?
Рефакторинг
Красный, зеленый, рефакторинг
Какие существуют виды тестов?
Инструменты
Платформы и инструменты для тестов TDD для проектов PHP
Преимущества TDD
Недостатки тест-ориентированной разработки
Когда применяется TDD
Измерение тестируемости с помощью покрытия кода
Практика использования TDD
Резюме

Развитие TDD

Подход TDD отличается от других методов тем, что он объединяет программирование с написанием тестов самим разработчиком. Эта концепция возобновляет всеобщее уважение к тестам, созданным программистом.

1976 год. Публикация Гленфорда Майерса «Надежность программного обеспечения», которая определяет, как аксиому то, что программист никогда не должен тестировать свой собственный код (Dark Age of Developer Testing).

1990 год. В дисциплине тестирования преобладают методы «черного ящика», в частности, в форме инструментов тестирования «поймай и повтори».

1991 год. Независимое создание тестовой среды в Taligent поразительно похожей на SUnit.

1994 год. Кент Бек пишет тестовый скелет SUnit для Smalltalk.

1998 год. Статья об экстремальном программировании упоминает, что «мы обычно пишем тест в первую очередь».

С 1998 по 2002 год. Test First преобразован в Test Driven.

  1. Автоматизированные тесты – инновационные методы, разработанные в этот период.

2003 год. Публикация Кента Бека «Разработка через тестирование: на примере».

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

Что же такое Test Driven Development (TDD)?

Разработка через тестирование – это подход к программированию, при котором программирование должно вестись на основании тестирования, а не наоборот. При традиционном подходе к разработке программного обеспечения заказчик выдвигает требования к программе. Затем разработчик создает код, который соответствует критериям приемлемости пользовательской истории. Затем код будет объединен и протестирован тестировщиком в среде QA или SIT. Тестер может найти дефекты. В этом случае код отправляется на доработку.

Существует цикл: написать код, запустить тесты, исправить код. Test Driven Development переворачивает этот цикл с ног на голову. В нем говорится, что мы должны сначала создать тесты, затем написать код, после чего исправить код.

В Test Driven Development сначала пишется и запускается тест, затем пишется код (до тех пор, пока он не пройдёт тест), а затем код реорганизуется. Это называется рефакторинг.

Рефакторинг

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

Функциональность может быть проверена во время рефакторинга путем многократного запуска тестов.

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

Красный, зеленый, рефакторинг

Принцип работы в программировании TDD суммирует Кент Бек в хорошо запоминающийся фразу: красный, зеленый, рефакторинг. Названия цветов относятся к пользовательскому интерфейсу популярного автоматического запуска теста JUnit, где красная полоса указывает на то, что тест не пройден, а зеленая полоса указывает на то, что тест прошел успешно.

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

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

Кент Бек, в eXtreme Programming (XP) (Beck 2000), определяет два простых правила для TDD:

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

Бек объясняет, как эти два простых правила генерируют сложное индивидуальное и групповое поведение:

  1. Разрабатывается работающий код, обеспечивающий обратную связь между решениями.
  2. Программист пишет свои собственные тесты, потому что не может ждать 20 раз в день, чтобы кто-то другой написал их для него.
  3. Среда разработки должна быстро реагировать на небольшие изменения (например, нужен быстрый компилятор и набор регрессионных тестов).
  4. Проекты должны состоять из множества тесно связанных между собой, чтобы облегчить тестирование (это также облегчает эволюцию и обслуживание системы).

Чтобы эффективно работать, разработчикам необходимо научиться писать эффективные модульные тесты. Бек считает, что хорошие юнит-тесты:

  1. Быстрые. У них короткие настройки, время работы и поломки.
  2. Модульные. Легко можно изменить порядок их прохождения.
  3. Используют данные, облегчающие их чтение и понимание.
  4. При необходимости используют реальные данные.

Какие существуют виды тестов?

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

  1. Проверка правильности работы системы на основе черного ящика.
  2. Работа в браузере – тестирует поведение системы, запустив ее в браузере и протестировав на реальном веб-сайте.
  3. Поведенческий – тестируют поведение объекта путем определения его функциональности.
  4. Тесты производительности – тестирование производительности системы при высоких нагрузках

Модульные тесты – тестирование приложения изнутри, с точки зрения программиста. Они тестируют код, выполняя тесты, проверяющие правильность работы библиотечных объектов / модулей / систем.

Цикличные – это непрерывное повторение создания модульных тестов, пока они не будут пройдены.

Эти типы тестов образуют Тестовую пирамиду.

Считается, что большинство тестов должны быть модульными, с меньшим количеством интеграционных тестов и ещё меньшим приемочных тестов. Google рекомендует соотношение примерно 70/20/10.

Инструменты

TDD должен сочетаться с хорошими инструментами. Необходима среда IDE, такая, как Eclipse с собственной поддержкой JUnit. Настоятельно рекомендуется использовать плагины для облегчения управления модульными тестами, такими как MoreUnit и Infinitest. Последний автоматически выполняет все модульные тесты при каждом изменении кода, что уменьшает циклы обратной связи, которые также закладывают основы для непрерывных модульных тестов. С другой стороны, использование шаблонов кода для модульных тестов является важной экономией времени в повторяющемся цикле TDD. На уровне кода для создания удобочитаемых и гибких бизнес-объектов необходим шаблон проектирования Builder.

Платформы и инструменты для тестов TDD для проектов PHP

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

Behat – это фреймворк BDD PHP с открытым исходным кодом, который использует “человеческие” предложения для запуска тестов программного обеспечения. Behat инструмент предложенный StoryBDD. Помогает определить описательные части, их потребности и вложенный в них смысл. Behat предлагает уникальный подход к тестированию.

Codeception – еще один надежный инструмент TDD для PHP. Codeception похож на PHPUnit и Behat, но все его тесты написаны на PHP, что делает процесс разработки более гибким. Во всех тестах Codeception можно использовать переменные и операторы, а также локаторы CSS и XPath.

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

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

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

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

Недостатки тест-ориентированной разработки

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

Не подходит для всех: метод отлично зарекомендовал себя для работы с небольшими проектами или даже с небольшими компонентами или функциями крупных проектов. К сожалению, методика может давать сбои при применении к очень объёмным или сложным проектам. Написание тестов для сложной функции, которую программист ещё полностью не представляет, может быть трудным, и даже невозможным. Написание тестов – это хорошо, но если эти новые тесты не совсем точно соответствуют требованиям функции, они бесполезны (или даже могут активно препятствовать разработке). Более того, некоторые проекты, особенно те, которые используют устаревший код или сторонние системы, просто не поддаются практическим методам. Для них практически невозможно создать тесты, которые должным образом интегрируются с этими системами или с устаревшим кодом.

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

Когда применяется TDD

TDD подходит как для нового программного обеспечения, так и для устаревших систем. Для команды разработчиков, которая имеет дело с существующим устаревшим программным обеспечением, работа начинается исправлением ошибок. Хорошей практикой является то, что для каждой сообщаемой ошибки создаётся тест, который устраняет ошибку, а затем исправляется функциональность. После нескольких итераций команда разработчиков создаёт повторяемый рабочий тест для исправления ошибок. Применяя эту методологию к новым программным приложениям, следует обратить внимание на инструменты тестирования, используемые для стека технологий. Например, при работе в приложении Angular, которое обычно использует среду тестирования Jasmine для модульного тестирования, и при использовании для Angular CLI, модульные тесты создаются вместе с модулями кода. Методология TDD будет заключаться в следующем:

  1. Определение части функциональности, которая будет создана с этим компонентом.
  2. Создание модульного теста, который не будет сразу настроен на эту часть функций.
  3. Тестовый прогон, чтобы подтвердить неудачный тест (здесь может быть полезно оставить тестовый прогон включенным после каждого сохранения исходного файла, что ускоряет процесс).
  4. Написание кода в компоненте Angular, который позволит выполнить тест.
  5. Внесение изменений рефакторинга для компонента Angular после подтверждения прохождения, используя тест в качестве руководства, чтобы гарантировать, что рефакторинг кода не нарушает функциональность.

Измерение тестируемости с помощью покрытия кода

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

  • Даёт чёткое представление об общей тестируемости, позволяя установить пороговое значение, гарантирующее, что общая тестируемость программного обеспечения не упадет ниже определенной точки.
  • Определяет области в кодовой базе с плохим тестированием, определяя их для рефакторинга.

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

Практика использования TDD

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

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

Резюме

Разработка через тестирование – это метод, который в настоящее время широко используется и становится все более популярным. Причина его популярности заключается в том, что усилия, необходимые для его освоения, не так велики, как для экстремального программирования, частью которого является TDD. Хотя у разработчика с самого начала должна быть дисциплина для написания и запуска тестов, без него не было бы программирования, управляемого тестами. Из-за своей природы метод может использоваться в сочетании с различными практиками разработки на более низком уровне (например, в течение одной итерации) и даже в более широком спектре проектов.

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

Источник

By Ruslan Novikov

Интернет-предприниматель. Фулстек разработчик. Маркетолог. Наставник.