Нет юнит-тестов? Нет проблем! |
12.12.2018 11:40 |
Автор: Катрина Клоки (Katrina Clokie) Перевод: Ольга Алифанова Какое-то время назад я создала опрос в Твиттере про юнит-тесты: "Код без юнит-тестов – это изначально плохой код? Да: 19% Возможно, да: 24% Зависит: 36% Нет: 21%" Последовавшие дискуссии покрыли ряд интересных моментов, которые опровергли некоторые мои предположения о юнит-тестах и том, как мы оцениваем код. Что такое плохой код?Формулируя исходный вопрос, я сознательно выбрала фразу "изначально плохой код". Я пыталась сделать акцент на том, что код будет объективно плохим, что отсутствие юнит-тестов однозначно сигнализирует об этом и будет одной из непредвзятых метрик оценки кода. В моей организации большая часть Agile-команд разработки включает юнит-тесты в свои критерии готовности. Практики Agile определяют критерий готовности, чтобы понимать, что требуется для завершения задания с приемлемым уровнем качества. В этом контексте отсутствие юнит-тестов – это то, что считается плохим знаком в Agile-команде. Критерий готовности может показаться непредвзятой метрикой, но это все-таки список, о котором совместно договорилось несколько человек. Код, который они создают, сам по себе не хорош и не плох. Он помечается как хороший или плохой на основании критериев, сформулированных этой группой для определения, что считать плохим или хорошим. Плохой код одной команды может быть хорошим кодом для другой, если их критерии готовности различаются. Плох ли код по определению, если он не делает того, чего хочет конечный пользователь? Необязательно. Что, если неожиданный результат все же полезен? В мире множество знаменитых продуктов, которые изначально предназначались совсем для иного – к примеру, пузырчатая пленка вначале продавалась в качестве обоев. Я убеждена, что нет такой вещи, как изначально плохой код. Очень важно понимать, как те, кто взаимодействует с вашим кодом, будут оценивать его полезность. Зачем писать юнит-тесты? Многие по умолчанию включают юнит-тестирование в общую стратегию, не задумываясь, какую информацию дадут эти тесты, какие практики используются при их создании, и какие риски они помогают снизить. Юнит-тесты обычно пишутся тем же разработчиком, который писал код. Их могут писать раньше, чем код – в разработке через тестирование, например – или после этого. Юнит-тесты определяют ожидания разработчика от поведения кода и проверяют "известное известное", или "то, о чем мы знаем и в чем разбираемся". Создавая юнит-тесты, разработчик должен вдумчиво поразмыслить о том, что код должен делать. Юнит-тесты ловят очевидные проблемы и мгновенно дают обратную связь через локальные запуски в процессе разработки. Если разработчик находит проблемы и решает их, пока код еще создается, это дает другим людям возможность находить неожиданные или интересные проблемы путем других форм тестирования. При наличии различных типов автоматизированного тестирования через точки интеграции или пользовательский интерфейс юнит-тесты дают возможность прогнать небольшой кусочек функциональности. Это особенно полезно при тестировании функции, которая по-разному себя ведет в зависимости от полученных данных. Вместо того, чтобы гонять все возможные варианты через более крупные тесты, можно встроить эти проверки на юнит-уровне. Юнит-тесты требуют, чтобы разработчик писал тестируемый код. Эти паттерны внедрения создают более устойчивый и простой в поддержке код. Когда релизная проблема требует рефакторинга существующего кода, наличие юнит-тестов может ускорить этот процесс, быстро сообщая, что код работает как должен. Существование юнит-тестов не гарантирует всех этих бонусов. Вполне возможно обладать кучей юнит-тестов, которые приносят мало пользы. Разработчик мог недопонять, как внедрять эти тесты, работать изолированно т остальных, или плохо справиться с тестовым покрытием. Значимость юнит-тестов часто зависит от командной культуры и других практик совместной разработки. Нет юнит-тестов? Нет проблем!В защиту создания юнит-тестов существуют очень весомые аргументы, однако их отсутствие – это необязательно плохо. В некоторых ситуациях выгоды юнит-тестов можно получить через иные инструменты. Внятные паттерны внедрения, облегчающие поддержку кода, можно навязать через инструменты статического анализа кода. Они требуют, чтобы код удовлетворял определенному формату и набору соглашений, отвергая все, что не удовлетворяет этой норме, до того, как код попадает в базу. Эти инструменты даже могут выявлять ряд функциональных проблем, отлавливаемых юнит-тестами. Вместо того, чтобы писать юнит-тесты, проверяющие поведение, о котором мы знаем, вы можете решить сдвинуть это тестирование на уровень интеграции. Если данные между зависимыми системами подразумевают множество вариаций, такой сдвиг поможет убедиться, что системы правильно взаимодействуют, не концентрируясь на отдельном компоненте. Сложность и время запуска тестов при этом возрастут, однако простые интеграционные тесты все еще будут давать быструю обратную связь разработке схожим с юнит-тестами образом. Имея дело с легаси-кодом, у которого нет юнит-тестов, не всегда имеет смысл ретроспективно их внедрять: игра может не стоить свеч. Аналогично, если код вряд ли будет меняться в будущем, усилия по созданию юнит-тестов могут не дать выхлопа по простоте поддержки кода, так как поддержка просто не понадобится. Между юнит-тестами и качеством кода может быть прямая связь, но одно не вызывает другое. Только потому, что эти два тренда, кажется, меняются в одних и тех же направлениях, не доказывают, что они значимо связаны друг с другом. |