43

Ответ на пост «Когда решил отрефакторить легаси»

Отвечая на комментарий, как готовить и с чем есть юнит-тесты, разразился целым постом.

Ответ на пост «Когда решил отрефакторить легаси» IT, Программирование, Разработка, Тест, Тестирование, Ответ на пост, Длиннопост

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

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

Юниты лучше всего отбиваются на редко изменяемых участках. Если у вас во всю фонтанирует стартапное безумие, то юниты будут как ярмо на шее, с которым ты пытаешься переплыть реку. Но когда все устаканится, лучше начинать их помаленьку подтягивать.

Есть еще такая штука - тестируемость кода. Код с кучей связности, долбанутым публичным интерфейсом, с огромными мультизадачными функциями и классами покрыть юнитами без тотального рефакторинга по сути невозможно. Есть такая прикольная корреляция: хороший код = тестируемый код. Т.е. если код соответствует SOLID, то он почти без вариантов тестируем юнитами (даже если его специально никто к этому не готовил). Те, кто живёт и кодит вне парадигмы тестирования, часто говнокодеры и городят такой ад, что когда они наконец осознают, что так жить дальше нельзя, они упираются в карфагенскую стену из гор нетестируемого спагетти. Кто-нить када-нить видел, чтобы говно на 100-200к строк кода взяли и отрефакторили? хотя бы наполовину? Для заказчика это звучит как переделать стадион посреди олимпийских игр. Дальше этот гроб остается только толкать вперед, пока он окончательно не сгниет или не провалится в чью-нибудь могилу. Чем все дружно и занимаются. Хотя разрабы здесь только выигрывают - они почти незаменимы (никто в этой помойке не разберется и во век, кроме авторов), но бизнес несет убийственные убытки (зачастую даже не понимая этого. Сравнить же не с чем, а своей экспертизы у высшего руководства обычно нет). Говнокодеры - это настоящие паразиты на теле человечества, поработившие планету.

Помимо рукожопых разрабов у тестов есть еще одна каста врагов - манагеры. Потому что они не понимают, как продать тесты. Ведь тесты - это где-то четверть всего времени, затраченного на разработку. Объяснить продавану-гуманитарию, зачем к сроку и бюджету надо накидывать еще минимум четверть, так же просто, как кота научить решать уравнения. А даже если каким-то невообразимым образом вам это удастся, все аргументы разобьются об «ой, да это просто макет, скриншот показать заказчику», «ой, да это просто POC, заказчику потыкать», «ой, да это ж MVP, он еще и не окупится», «ой, заказчик хочет еще вот тут кнопку добавить», «ой, не, мы не можем щас выделить на это время - заказчику срочно нужно вот это ТЗ сделать к четвергу», «ребята, я понимаю, что уже 100к строк кода и вы работаете со скоростью улитки и все валится. Но заказчик не выделит ресурсов на рефакторинг. Стискиваем зубы и ползем дальше». The end.

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

Не пытайтесь покрыть каждый сантиметр. В тестах можно утонуть, как и в лапше из говнокода. В первую очередь кройте сложную логику, критические куски. Остальное - как пойдет. Не пойдет - ок, на 80% мы застрахованы, с дороги этот поезд уже не сойдет.

В начале проекта юниты часто не нужны вообще. Пока кодовая база не устоялась, покрывать ее низкоуровневыми тестами - это трата ресурсов в никуда. Отдавайте предпочтение более высокоуровневым тестам, которые на этом этапе проще в реализации и эффективнее. Пример. Бэкенд апи. Фаза активной разработки. Все переписывается чуть ли не ежедневно, даже толком спеки устоявшейся нет. Начнете крыться юнитами - затянете сроки. Что можно сделать? На помощь приходят функциональные тесты.

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

Когда кейсов станет слишком много, а запуск функционального цикла тестирования начнет занимать неприлично много времени (по 20 минут ждать кому-нибудь охота?), они уступят место юнитам как основе, отъехав на более поздние этапы тестирования - такие как предрелизный прогон на CI. Как ни крути, но юниты - это суррогат. Юниты тестят хрен знает что, хрен знает как и не гарантируют, что юзеру вообще вернут ответ, потому что какой-нибудь высокоуровневой функции фреймворка захотелось упасть с исключением из-за одного символа в теле запроса. Функц. тестирование же напротив очень надежное, т.к. максимально эмулирует реальный мир, но порой слишком громоздкое, чтобы его было удобно юзать в процессе разработки.

Есть еще такая крутая тема как TDD. Это когда ты пилишь тест до собсно кода. Т.е. продумал тест, запилил, пошел реализовывать код. В реальности, на 100% буквально сию парадигму юзать сложно, да и не нужно. Но TDD приучает к очень полезной привычке - проектированию кода, который будет легко тестируемым. Потому что заблаговременно продумав тестирование класса его потом покрыть гораздо легче, нежели когда ты устроил вакханалию с зависимостями, а потом такой «бле, а как терь его вызывать-то вообще, не эмулируя половину кода». По сути, TDD заставляет продумывать тестирование на как можно более раннем этапе проекта. Даже если проект пилится сеньорскими сеньорами, спящими с книжками по SOLID'ам и GRASP'ам под подушкой, в будущем можно упереться в не очень удобную для тестирования архитектуру. Так что есть смысл думать об этом заранее, чтобы потом меньше чертыхаться и мечтать слетать в прошлое, чтобы чутка вот эту фиговину сделать по-другому.

Показать полностью 1
3500

Когда решил отрефакторить легаси

Отличная работа, все прочитано!