Большим разочарованием для меня была ситуация, когда проект начинался с огромным воодушевлением, а затем, с течением времени, код становился все более отвратительным. Год спустя у меня, как правило, формировалось устойчивое желание уйти из проекта, чтобы никогда в жизни не иметь дела с этим гнусно пахнущим кодом. TDD позволяет с течением времени поддерживать уверенность в коде. По мере того как тестов становится все больше (а ваше мастерство тестирования улучшается), вы обретаете все большую уверенность в том, что система ведет себя именно так, как вам надо. По мере того как вы улучшаете дизайн, становится возможным все большее количество изменений. Цель TDD заключается в том, чтобы через год работы мне было бы интереснее и приятнее работать над проектом, чем в самом начале проекта.
TDD помогает командам разработчиков создавать хорошо спроектированные, удобные в сопровождении системы с чрезвычайно низким уровнем дефектов/ошибок.
Снижение количества дефектов/ошибок в коде, вызывает множество вторичных психологических (меньше нервозность, меньше беспокойства и стресса) и социальных эффектов. Взаимоотношения с партнерами по команде становятся более позитивными. Разработанный код перестает быть причиной сбоев, люди стали в большей степени рассчитывать на него. У заказчиков тоже повышается настроение. Теперь выпуск очередной версии системы означает новую функциональность, а не набор новых дефектов, которые добавляются к уже существующим.
Даже хорошие программисты ошибаются. Разница между хорошим и плохим программистом в том, что хороший программист использует тесты, чтобы обнаружить свои ошибки как можно раньше. Чем раньше вы проверите свой код на наличие ошибки тем больше ваши шансы найти её и тем меньше будет стоить её поиск и исправление. Это объясняет почему тестирование становится большой проблемой если его откладывать до релиза. Большинство ошибок не будут найдены совсем, а цена исправления тех, которые были пойманы, будет так высока, что вам придётся отобрать (и исправить) только некоторые из них, потому что вы просто не сможете себе позволить исправить их все.
Тестирование с помощью PHPUnit не слишком отличается от того, что вы уже, должно быть, делаете. Это всего лишь другой способ делать это. Разница между тестированием, то есть, проверкой того, что ваша программа ведёт себя так как ожидалось, и выполнением набора тестов, выполняемых фрагментов кода, которые автоматически проверяют правильность частей (модулей или юнитов) ПО. Эти исполняемые фрагменты кода и есть юнит-тесты.
Цель использования автоматических тестов в том чтобы делать меньше ошибок. И хотя ваш код всё еще будет несовершенен, даже при отличных тестах, вы скорее всего увидите разительное снижение количества дефектов как только вы начнёте автоматизировать тесты. Автоматизированные тесты дают вам обоснованную уверенность для того, чтобы предпринимать более смелые шаги в дизайне (Рефакторинг), лучше работать с товарищами по команде (Командное Тестирование [Cross-Team Tests]), улучшить отношения с вашими клиентами, и каждый раз вечером уходить домой с уверенностью что система стала лучше чем она была утром благодаря вашим усилиям.
Тесты должны быть одновременно:
Простыми в изучении.
Если будет сложно научиться писать тесты, разработчики не будут учиться писать тесты.
Простыми в написании.
Если тесты будут сложными при написании, разработчики не будут их писать.
Легко читаемыми.
Тестовый код не должен содержать дополнительной сложности, так чтобы тест сам по себе не потерялся в шуме который его окружает.
Легко исполняемыми.
Тесты должны запускаться по нажатию одной кнопки и представлять результат в ясном и недвусмысленном формате.
Быстро исполняемыми.
Тесты должны выполняться быстро чтобы их можно было запускать сотни или даже тысячи раз в день.
Изолированными.
Тесты не должны влиять друг на друга. Если порядок запуска тестов меняется результат тестов должен оставться неизменным.
Комбинируемыми.
Мы должны иметь возможность запускать совместно любое количество тестов в любых комбинациях. Это является естественным следствием изоляции.
Существуют два основных противоречия между этими требованиями:
Простота изучения против простоты написания
В основном тесты не требуют всей гибкости языка программирования. Многие инструменты тестирования предоставляют свой скриптовый язык, который включает только минимально необходимые функции для написания тестов. В результате, тесты становится легко писать и читать т.к. в них нет ничего лишнего, что отвлекало бы от содержимого самих тестов. Однако, изучение еще одного языка программирования и набора инструментов неудобно и загромождает память.
Изоляция простив скорости исполнения.
Если вам нужно чтобы результаты одного теста никак не влияли на результаты другого, каждый тест должен запоминать в полном объёме состояние окружения до своего начала чтобы вернуть окружение в его оригинальное состояние после своего завершения. Однако, восстановление окружения может занять долгое время: например, соединение с базой данных и инициализация её в заданном состоянии с использованием реалистичных данных.
PHPUnit жертвует скоростью выполнения тестов в пользу изоляции. Изолированные тесты важны, потому что они обеспечивают высокое качество обратной связи. Вы не получите отчёт с кучей ошибок, только потому что один тест в начале пакета провалился и оставил окружение поломанным для остальных тестов. Такая ориентация на изоляцию тестов приводит к архитектуре с большим числом простых объектов. Каждый объект может быть протестирован быстро и изолировано. Результат - лучшая архитектура и более быстрые тесты.
PHPUnit предполагает, что большинство тестов успешны и они не стоят подробного отчёта. Однако, если тест провален, то этот факт стоит отметить в отчёте. Большая часть тестов должна быть успешна и она не стоит комментариев, кроме подсчёта количества выполненных тестов. Это предположение по сути встроено в логику классов отчётов, а не в ядро PHPUnit. При отображении результатов запуска тестов вы видите сколько тестов выполнилось, но только для проваленных тестов вы увидите детали.
Ожидается что тесты "мелкозернисты" и каждый будет тестировать отдельный аспект одного объекта. Таким образом, как только тест падает его выполнение прекращается и PHPUnit сообщает о неудаче. Умение протестировать [систему] с помощью множества маленьких тестов - настоящее искусство. "Мелкозернистые" тесты улучшают архитектуру системы.
Когда вы тестируете объект с помощью PHPUnit вы работаете только через публичный интерфейс. Тестирование только публично доступного поведения способствует выявлению и решению сложных архитектурных проблем до того как последствия плохого дизайна заразят большую часть системы. Сказано хорошо, речь идет о том, что защищенных методов не должно быть или их должно быть мало, а это значит что защищенные методы нужно выносить в отдельные классы, что не всегда уместно и правильно (иногда просто неразумно или очень сложно разделить что-то единое на две и более частей).
Комментарии
Комментариев пока нет.