Unit tests

Продолжение первой части материалов о Unit tests (UT), опубликованной более месяца назад.

Почему люди в 2017 году все еще не пишут тесты?

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

Закончив с лирическим отступлением, можно выделить 2 основные мысли:

  1. Юнит тестирование не является чем-то особенным, чем-то посторонним, дополнительным. При разработке мы постоянно тестируем код, потому что так построен процес мышления и разработки. Это итеративный процесс к написанию кода который выполняет определенную задачу. При этом мы постоянно придумываем test cases, анализируем как код выполняет одну или другую задачу. В процессе разработки мы постоянно создаем test cases и постоянно их выполняем сравнивая результат с тем, что мы должны получить. Практически разница между автоматическим юнит тестом и тем тестированием которое мы делаем при разработке в своей голове заключается лишь в том, что мы его куда-то записываем — unit test как код. Нет никакого другого дополнительного процесса написания unit tests, он всегда является неотъемлемой частью разработки.
  2. Юнит тестирование не является чем-то выдуманные просто так или для забавы разработчика. Оно являются средствам исполнения и реализации определенных требований к системе. Любая система кроме функциональных требований содержит и нефункциональные, которые называются quality атрибуты (атрибуты качества). Например, производительность или доступность системы. Есть еще два свойства системы называемых modifiability i maintainability. Modifability отвечает за то, что система модифицыруема, тоесть если в нее нужно внести изменения — то в нее возможно внести эти изменения, причем в определенные сроки. Это дает уверенность заказчику что можно будет добавить новую функциональность и потратить на это примерно три месяца. Если заказчик узнает что изменение нужно вносить три года — это нарушение атрибута modifability. Maintainability — систему можно поддерживать. Если в ходе использования системы в ней нашлась ошибка — ее можно исправить. И ее можно исправить в определенный временной промежуток. Ожидается, что критическая ошибка может исправиться за 2 дня, но если оказывается что ее невозможно исправить — это также нарушение нефункциональной требования качества системы.

Юнит тесты — это один из инструментов как удовлетворить нефункциональные требования modifability i maintability. Это придает гибкости системе, ведь ее нужно менять. Иногда нельзя внести изменения потому что мы не понимаем зачем это было сделано. У нас нет юнит тестов чтобы посмотреть кейсы зачем это было сделано. Или мы не можем внести изменения так как не знаем какие другае части системы будет разрушено. Или код, который не имеет юнит тестов, более сложный, он менее changeable.

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

Почему программисты все еще не пишут юнит тесты?

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

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

Почему заказчикам нужно доказывать что нам нужны Unit tests?

Большинство разработки на постсоветском пространстве представляет собой outsource, причем, в одном из его худших проявлений — когда клиент решает как мы будем работать. Он смотрит на то как сам девелопив легаси системы 20 лет назад и говорит что разрабатывали без каких-либо юнит тестов и все было нормально. А мы со своей стороны не можем продать это правильно, не умеем донести это как неотъемлемую часть разработки.

Продавая клиенту разработчиков, мы говоришь что вот это замечательные кодеры, они пишут прекрасный код и к нему же юнит тесты и интеграционные и проводят код ревью, но это все отнимает дополнительное время. На вопрос «Сколько это отнимает времени?» кто-то может сказать, что процентов 30. Для заказчика это же значит что 30% времени девелоперы, вместо того чтобы делать его продукт, будут заниматься чем-то еще другим, что ему не смогли уверенно продать. Конечно же он будет говорить чтобы это убрать. Ну а мы как всегда говорим «Клиент всегда прав», и убираем тесты.

Писать UT или не писать?

Это вполне индивидуальный вопрос, не зависит от менеджмента или заказчика. Например, Роберт Мартин приводит пример с мытьем рук для хирурга. Пациент никогда не приходит и не спрашивает что врач будете делать и сколько это будет стоить, на что он тратит деньги за операцию? А давайте без мытья рук, но дешевле — это ведь даже не рассматривается. Мы специалисты которые делают свою работу качественно, и если для этого нужно написать UT — то нужно их писать.

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

Как внедрить UT на уже существующий проект?

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

Тесты будет трудно писать для системы которая разрабатывалась без testability in mind. Поэтому можно начать с практики — давайти писать тесты на весь новый код который мы пишем. И в этом случае ни у кого не будет отмазки что на этот код невозможно написать тесты — «я потрачу 3 дня в рефакторинг чтобы написать тесты». Нужно начинать с малого. Нельзя на проекте котором писали 3 а может и 5 лет без тестов взять и написать на все тесты.

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

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

Многие ответы можно найти в книге Working Effectively with Legacy Code от Michael Feathers. Также рекомендую:

Как нам понять что есть какая-то польза от написания Юнит тестов?

Простого ответа на это нет. За годы комьюнити уже доказало что юнит тестирования приводит ко многим положительным результатам.

Прежде чем писать UT нужно дать самому себе ответ на вопрос — «А зачем нам нужны UT?». Поэтому перед началом проводится фаза разбора текущих проблем на проекте и root cause анализ. Мы стараемся понять в чем проблема и могут ли ее решить юнит тесты. Построив дерево проблем, которое исходит из необходимости юнит тестов, можно увидеть пользу от UT, потому что по веткам этого дерева должно стать легче. Если какая-то ветка вела от дефекта, то начав писать юнит тесты, этих дефектов должно становиться меньше. Если программисты боялись делать рефакторинг, то теперь можно замерить насколько проще это стало делать.

Эффективность от тестов мы будем чувствовать когда нужно будет что-то изменить. Когда код покрыт тестами то мы не волнуемся что-то сделаем не так. Тесты всегда покажут работает ли код все после изменений.

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

Что такое code coverage и как правильно его использовать?

Данная метрика только помогает и никогда не должна определять. Это не должна быть метрика менеджера или кастомера. Данная метрика должна помогали только разработчику понять работает ли он хорошо или плохо. Она никогда не определяет, тесты написаны хорошо, даже если покрытие 100%. Code coverage — это инструмент разработчика для разработки а не менеджера для оценки чего-либо.

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

Цифра покрытия, по сути, ничего не значит. Значение имеет отчет, который мы читаем и понимаем. Он показывает куски кода которые не покрыты. Можно увидеть что какой-то кусок кода критический и нуждается теста, другой же не критичен и не нуждается. Покрытие должно показывать код который еще не покрыли. Процент покрытия может составлять и 90%, но есть 10% критического кода который не покрытый. В этом случае использование code coverage неверное.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Похожие записи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *