Alek�ei Matiu�hkin

сделано с умом



Про тесты

Tuesday, 22 Aug 2023 Tags: 2023pro

Я решил еще немного поговорить про свою профессию, примеры из которой столь хорошо применимы к обычной жизни, раз уж даже про код ревью кому-то показалось интересным. Раньше, когда деревья были зелеными, (или, как любил говорить один из моих педагогов, «когда вопрос стоял и деньги были»), программисты были людьми умными, кропотливыми и ответственными. Они писали код, который потом доводил Аполло до Луны, проверяя его качество при помощи дополнительной кружки кофе и пары красных глаз коллеги. С тех пор утекло много воды, программисты поглупели, обленились, и вот уже около двадцати пяти лет коварный менеджмент заставляет помимо кода писать тесты, которые призваны аккуратнее, чем честное слово разработчика, проверять, что код делает именно то, что было задумано.

Известно, впрочем, что программисты славятся (помимо непомерных зарплат) в основном ленью и изворотливостью, поэтому мы обычно пишем тесты примерно так, как писали в школе объяснительные за прогулы: чтобы точно проканало. Мы тщательно подбираем красивые входные параметры, которые не выходили у нас из головы с момента начала работы над реализацией того, или иного алгоритма. Спроси добрую половину синьёров рест-девелоперов (известных в узких кругах как джейсоноукладчики), что произойдет в результате работы их программы, если вместо ожидаемого запроса GET /foo/42 придет запрос GET /FOO ④②, и вы будете изумлены многообразием нерешительных (и, чаще всего, неправильных) ответов.

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

Итак, тесты. Обычно различают три категории: юнит-тесты, интеграционные и нагрузочные. Я прямо слышу негодующий ропот профессионалов: эта классификация неполная, неверная, дилетантская. Да, так и есть. Но это мой текст и я позволю себе навести аксиоматику так, как считаю нужным. Можно тестировать отдельные модули, можно тестировать их взаимодействие, и можно тестировать, как оно все работает, когда вокруг — пожар посреди цунами. Все остальное — лишь ненужная детализация.

Юнит-тесты нужны в основном для того, чтобы было удобнее описать требуемую функциональность, протестировать каждый маленький кирпичик отдельно и удостовериться, что хотя бы без помех он работает как ожидается. Если продолжить аналогию с каменщиком, юнит-тест — это нивелир в руках печника; уложив кирпич, с его помощью можно сразу удостовериться, что кирпич лежит ровно, и поиграться с раствором (пока тот не засох), если нет. Большого смысла в применении нивелира после того, как печь сложена — нет.

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

Безусловно нужно упомянуть так называемые «property-based» тесты, которые буквально проверят, что произойдет, если печь топить не только дровами, но и рубероидом, шифером, куриными костями, шпалами и рельсами. На самом деле, по моему опыту это единственный тип тестов, который позволяет действительно отловить важные ошибки на ранней стадии, я их использую всегда и настоятельно рекомендую всем, кто готов меня выслушать.

Нагрузочный тест (не жалеть дров, пока крышка духовки не покраснеет) позволит определить расход топлива и максимальную мощность печи. При использовании совместно с интеграционным тестом «кот в комнате», станет понятно, насколько можно прикрывать заслонку при полной загрузке.

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

▸ Количество результатов. Вводим запрос, проверяем, что количество результатов более десяти. — В принципе, в самом по себе тесте нет ничего плохого: если его совместить с property-based тестированием (то есть генерировать запросы случайным образом), можно даже выловить проблемы, возникающие при идиотских запросах, которые сам программист в тест никогда не добавит, потому что сам не знает, как его код на это отреагирует, и втуне стесняется (или даже побаивается) узнавать. Но, по гамбургскому счету, смысла в такой проверке не больше, чем в проверке качества печи подсчетом количества потраченных на нее кирпичей. В реальной жизни аналогом такой проверки является отговорка «хуже не будет».

▸ Детальный анализ результатов определенного запроса. — Поисковая система, которая умеет идеально подбирать выдачу по строго определенному заранее запросу, — полезна не более, чем стоящие часы Алана Милна; те тоже умеют показывать абсолютно правильное время дважды в сутки, надо лишь не забыть на них вовремя посмотреть. В жизни — это тщательные детализация и разбор «что будет» какого-то одного из миллиона вероятных исходов.

▸ Миллиард тестов, проверяющих детали реализации. — Во-первых, это красиво, когда метрика test coverage ярко светит зелененьким и показывает недостижимые в нормальных проектах 100. Во-вторых, удостоверяться, что выборка десяти отсортированных по дате документов возвращает десять последних документов — это паранойя. На встречах анонимных педантов — уместна, в живом проекте — сделает любой рефакторинг фактически невозможным. В обычной жизни проецируется на ситуацию, когда из точки А в точку Б доехать быстрее с пересадкой в Д, но человек выбирает длинный крюк с пересадкой в В, потому что уже ездил по такому маршруту. Урон незначительный, но его можно избежать.

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

▸ Тесты с вовлечением сторонних сервисов. — См. предыдущий пункт. Песочница, которую вы не можете проконтролировать — ничем не лучше заглушек и моков. Не нужно даже пытатся общаться с внешним миром: просто всегда полагайтесь на то, что сторонний сервис написан злобным психопатом, который вас ненавидит. В жизни просто выбирайте самый простой вариант поведения которое вам зачем-то требуется спрогнозировать.

▸ Нагрузочные тесты: никогда не пренебрегайте ими. — Даже если сейчас у вашего сервиса всего три пользователя, завтра каждый из них может привести по сто друзей. Не нужно проектировать системы сразу в расчете на то, что имя вашим пользоватям — легион, но примерно знать, что вы станете делать, когда их количество увеличится в сто раз — необходимо. В жизни — надо помнить, что человек предполагает, а располагает всегда кто-то другой — и иметь вариант «Б» на случай пожара.

▸ И, наконец, методики. DDD, TDD, ХренDD — это все от лукавого. — Придумано людьми, которые зарабатывают деньги придумыванием методик и их распространением. Слышите слово «коуч», или «евангелист», или «адвокат» — бегите (если только это не адвокат по уголовным делам, а вы не задержаны по подозрению в порче интерфейса другого адвоката). Все хорошо в меру, смешивайте, но не взбалтывайте. TDD очень удобен, когда нужно добавить пару кнопок в приложеньице, но упаси вас мироздание пытаться применять TDD с самого начала при создании новой библиотеки. В жизни это означает, что лучше всегда пользоваться своей головой, а не слепо следовать написанному в книге «Карнеги и его друзья».

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


  ¦