В чём основная задача теста
Разработка через тестирование (test-driven development, TDD) появилась в 1999 году. С помощью теста можно подтвердить, либо опровергнуть работоспособность кода.
Основной частью разработки программного обеспечения является — написание работающего кода. Основная задача написания теста для будущего кода, чтобы этот самый код работал адекватно, а не «криво». Получается не имеет смысла тестировать уже готовое ПО, с «кривым» кодом. Тестирование помогает на всех этапах держать под контролем процесс оценки того, что все части приложения ведут себя так, как ожидалось. Полученный результат соответствует спецификациям, учитывая разные входные данные. Каждая из этих проверок называется тестовым сценарием (test case).
Проверка кода
При TDD разработчику необходимо создать автоматизированные модульные тесты с требованиями к коду перед написанием своего кода. Тест проверяет условия, которые либо выполняются, либо нет. При выполнении тест считается пройденным, что подтверждает условие работоспособности кода. Модульные тесты самые уязвимые участки кода. Указывают разработчику на слабые места в коде, который в будущем будет часто изменяться.
Ключевые причины сначала писать тесты, а не код
1. Для сокращения времени и объёма написания кода
Вам требуется написать такой объём кода, чтобы он прошел тест. При прохождении теста, вы закончили с этой частью работы. Занимаясь реализацией крупной фичи, легко потеряться в коде. Тестирование помогает не сбиваться с пути и сохранять концентрацию при написании кода для этой фичи.
При работе над крупным и быстроразвивающимся проектом, прежде чем перейти к работе над следующей частью функционала, нужно закончить ту, над которой вы уже начали работать. Здесь не может быть никаких «вернусь к этому позже», потому что вы не сможете продолжить писать код, пока не пройдёте тестирование. Этот подход притормаживает ваше движение. Так вы можете действительно хорошо обдумать, что собираетесь писать, прежде чем начинать писать. А когда у вас будет хорошо составленный тест, вам будет несложно написать реализацию своего решения.
2. Для экономии времени на отладку и поддержку
На тестирование уходит слишком много времени. Порой целые дни уходят на поиск неверной логики, зато лишние несколько часов на тесты некоторые разработчики считают потерянным временем. А между тем, при таком подходе отладка существенно облегчается, ведь вы работаете над отдельным куском кода, не зависящим от всего остального.
При обновлениях можно не беспокоиться о регрессионном тестировании. Несколько часов, потраченные на написание тестов, могут избавить вас от ужасных проблем.
3. Для подстраховки и спокойного сна
Бывает представители бизнеса не очень-то хотят, чтобы разработчики тратили время на написание тестов.
Если кто-то сделает деплоймент (выгрузку файлов в место, откуда они будут раздаваться.) изменений без вашего ведома. Тесты спасут вашу систему. С их помощью вы докажите, правильность написанного кода. А значит поломку спровоцировал кто-то без вашего ведома. Тесты это ваша страховочная сеть, потому что они защищают вас от многих проблем, служа доказательством вашей невиновности.
Написав заранее тесты и запуская их каждый раз, когда добавляете новый код, вы обеспечиваете себе четкое подтверждение того, что все работает, как надо. Ничто другое не даст вам подобных доказательств вашей правоты.
Другие причины написания тестов для своего кода:
- Получить доверие к коду;
- Для составления документации;
- Проводить рефакторинг без «сюрпризов»;
- Чтобы не облажаться перед коллегами на проекте;
- Чтобы проект мог долго жить и развиваться (добавлять новые фичи и исправлять баги).
Очевидные преимущества тестов:
- Экономия бюджета;
- Обеспечение безопасности кода при командной работе;
- Помощь в создании лучшей архитектуры.
Как происходит разработка через тестирование
Перед написанием теста разработчик сначала рассматривает всевозможные сценарии использования. Добавляемые новые требования могут также повлечь изменение существующих тестов. Именно это отличает разработку через тестирование от других техник, когда тестируется готовый код. Метод TDD позволяет разработчику сфокусироваться на требованиях до написания кода.
Существует также разработка через приёмочное тестирование (ATDD — acceptance test-driven development) в котором критерии, описанные заказчиком, автоматизируются в приёмочные тесты
Модульное тестирование (UTDD — unit test-driven development) позволяет гарантировать, что приложение удовлетворяет сформулированным требованиям. При разработке через приёмочное тестирование, команда разработчиков сконцентрирована на чёткой задаче: удовлетворить приёмочные тесты, которые отражают соответствующие требования пользователя.
Приёмочные (функциональные) тесты (customer tests, acceptance tests) — тесты, проверяющие функциональность приложения на соответствие требованиям заказчика. Приёмочные тесты проходят на стороне заказчика. Это помогает ему быть уверенным в том, что он получит всю необходимую функциональность.
Типы тестов
Существует несколько типов тестов, каждый из которых имеет свою цель и предназначен для определённой области действия.
При постоянном усовершенствовании кодовой базы, шансы сделать ошибку повышаются, потому что база становится более сложной. А когда она выходит в релиз, цена ошибки увеличивается.
Основные типы тестов
Модульные тесты предназначены для тестирования изолированно определённой функциональности. Их легче всего создавать и обслуживать, поэтому большинство тестовых наборов — это модульные тесты.
Интеграционные тесты нужны для того, чтобы убедиться, как несколько зависимых друг от друга модулей кода работают вместе. Их взаимодействие работает должным образом.
End-to-end-тесты — E2E или сквозные тесты. Тестируют систему в целом, эмулируя реальную пользовательскую среду. В интернете это тесты, запущенные в браузере, имитирующие щелчки мышью и нажатия клавиш.
Интеграционные тесты можно разделить на ещё несколько подвидов тестов
Наиболее распространены тесты компонентов и API. Это особые типы интеграционных тестов, которые пишутся на стороне фронтенда.
API (Application Programming Interface) — интерфейс программирования приложений. Представляет собой совокупность инструментов и функций в виде интерфейса для создания новых приложений, благодаря которому одна программа будет взаимодействовать с другой. Это позволяет разработчикам расширять функциональность своего продукта и связывать его с другими. API включает в себя: операции, данные на входе и на выходе.
При работе над долгоживущим проектом, перед разработчиками стоят две принципиально главные задачи:
1) Сделать систему максимально простой для заданных бизнесом требований;
2) Постоянное тестирование системы.
Линтинг
Порой можно сказать, насколько код корректен, даже не запуская его. Процесс исследования исходного кода без запуска называют линтингом, а программу, которая это делает — линтером.
Линтер разбирает исходный код на стандартизированные кусочки, а потом даёт эти кусочки на проверку специальным плагинам. Плагин получает разобранный участок кода и проверяет его на корректность по ряду правил, которые определены в этом плагине. Линтер пытается заполнить пробел, предоставляя правила проверки ошибок синтаксиса, стиля кода и неправильного использования (проблемных паттернов). В результате он уменьшает количество ошибок и повышает качество и корректность вашего кода. Результаты работы линтера — это не абсолютная истина. Скорее подсветка спорных моментов, на которые автору кода стоит обратить внимание.
Типичные проблемы, которые показывают линтеры:
- неоднозначности, которые понятны автору кода, зато путают других разработчиков;
- особенности языка, которые легко забыть, но просто проверить автоматически;
- потенциальные проблемы производительности (например, ожидание асинхронной функции в цикле);
- использования не принятых в языке конструкций (например, неявное приведение типов).
Недостатки test-driven development
Есть задачи, которые не решаются при помощи тестов. TDD не позволяет механически продемонстрировать адекватность разработанного кода в области безопасности данных и взаимодействия между процессами. Некоторые проблемы, возникающие в области взаимодействия между процессами, невозможно с уверенностью воспроизвести, просто запустив некоторый код.
Модульные тесты, создаваемые при разработке через тестирование, по большей части пишутся теми же, кто пишет тестируемый код. Если разработчик неправильно истолковал требования к приложению, и тест, и тестируемый модуль будут содержать ошибку.
Большое количество используемых тестов может создать ложное ощущение надёжности, приводящее к меньшему количеству действий по контролю качества.
Уровень покрытия тестами, получаемый в результате разработки через тестирование, не может быть легко получен впоследствии. Исходные тесты становятся всё более ценными с течением времени. Если неудачная архитектура или стратегия тестирования приводят к большому количеству не пройденных тестов, важно их все исправить в индивидуальном порядке. Простое удаление, отключение или поспешное изменение их может привести к необнаруживаемым пробелам в покрытии тестами.
Выводы
Нужно ли покрывать весь код тестом т.е. абсолютно всё тестировать?
Стопроцентное покрытие кода тестами все равно не гарантирует, что в коде нет багов. Тем не менее чем выше процент покрытия, тем надежнее выглядит код и тем безопаснее вносить в него изменения. При написании тестов следует придерживаться правила, что качество лучше количества.
С какими нюансами при написании тестов сталкиваются наши разработчики
Можно сказать, что код написанный без тестов сразу становится легаси. Нашим разработчикам в своей работе часто приходится работать и с таким кодом. При добавлении фичи в модуль без тестов помимо самого нового функционала приходится писать тесты на зелёные сценарии использования модуля, так сказать отдавать технический долг за тех, кто его занимал.
#техтема_пф
Ещё больше интерсных материалов в телеграм канале и группе в Вконтакте