LINUX.ORG.RU

Одержимость покрытием юнит-тестами

 


0

4

Дисклеймер раз. Кликбейтность заголовка - не постановка диагнозов, а провокация дискуссии. Дисклеймер два. Все нижесказанное не касается TDD и прочего test-first. Разговор о том, когда тесты пишутся после.

Пишу на Джаве за деньги четвертый год. Последние полтора года имею дело с «покрытием юнит-тестами». Как правило это что-то типа 80% покрытия, юнит - публичный метод класса. И чем дальше имею с этим дело, тем больше закрадывается подозрение, что подобное покрытие юнит-тестами - напрасная трата времени. Главная причина - хрупкость этих самых тестов. Я буквально могу перечислить по пальцам одной руки случаи, когда тесты краснели из-за пойманных ошибок. В 99% случаев это происходит потому, что в процессе изменений продуктового кода меняются интерфейсы юнитов (сигнатуры методов). И тесты приходится поддерживать, поддерживать и поддерживать. Чтобы если повезет, когда-нибудь увидеть действительно полезный не прошедший тест.

Есть точка зрения, что юнитом в юнит-тестировании должен быть не отдельный метод, а некая логическая единица работы, часто она будет включать в себя несколько методов и даже классов. Но и тут есть определенные нюансы. Количество моков (моки ведь популярны сегодня?) в таких тестах может быть не самыми приятными.

Складывается ощущение, что «code coverage» с развитием CI/CD стал вездесущ и очень часто от него попахивает карго-культом. При этом я не отрицаю пользу юнит-тестов как таковых. Считаю их полезными, когда юнит устойчив и содержит в себе некую ключевую логику, которую действительно имеет смысл тестировать атомарно.

Что скажете?


Ответ на: комментарий от Raziel

Можно уточнить, что вы подразумеваете под интеграционными тестами? Условно говоря, это тест Spring контроллера вместе с БД? А зависимости на внешние сервисы - мокаются?

Все так, тесты контроллеров вместе с БД, а внешние сервисы мокаются.

Aber ★★★★★
()
Ответ на: комментарий от evgeny_aa

Я в ближайший год уйду из писательства кода за деньги, потому что эта сфера - сплошной маразм, долбоебизм и подверженность модным веяниям, которые пользы зачастую не несут, а только вред. К юнит-тестам тоже относится (я не говорю, что они не нужны, если вдруг кто-то неверно поймёт меня).
Разработка должна быть хобби

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

byko3y ★★★★
()
Ответ на: комментарий от urxvt

Это, может, когда у тебя твой сервис от других не зависит.

«Но случаи их применения очень чётко ограничены — зависимость от неподконтрольных систем и длительное время работы внешнего компонента. Всё остальное косяки, особенно это касается моканья базы данных.» (с)

Да и моканьем я тут называю не только условный Mockito, но и поднятие всяких БД с вставкой туда каких-то предопределенных значений.

Неправильно называешь. Мокируется – логика, а в базе данных – данные, строго аналогично данным в @DataProvider для обычных тестов. Вот если бы ты вместо коннекта к реальной базе данных замокировал бы обращения к ней…

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 2)
Ответ на: комментарий от dimgel

Ну правильно, не правильно это спорный момент. Везде терминология разная. Смысл в том, что работает не реальный код, который потом ты и будет крутиться в проде, а фабула. На сколько фабула близка к прод коду это уже другой вопрос. Правда, если ты про поднятие прямо реального Постгреса то да, но это другие тесты. Я имел ввиду поднятие всяких фабул, типа H2, Fongo, и т. п.

urxvt ★★★★★
()
Ответ на: комментарий от urxvt

Ну правильно, не правильно это спорный момент. Везде терминология разная. Смысл в том, что работает не реальный код, который потом ты и будет крутиться в проде, а фабула.

Блин, ну харе уже бредить. При заполнении реальной базы тестовыми данными работает реальный код, к этой базе коннектящийся, а не моки. Терминология тут вполне однозначна и очевидна.

dimgel ★★★★★
()
Ответ на: комментарий от urxvt

UPD. Сорри, не дочитал. Заменять для тестов постгрес другим SQL-сервером – ну может и можно назвать мокированием. Но по мне это больше смахивает на бред собачий. Отличия SQL-серверов достаточно существенны, чтобы такая замена убила весь смысл тестирования, а поднять постргесовскую базу в tmpfs для тестов – раз плюнуть.

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 3)

Одержимость покрытием юнит-тестами

Экзорцизм поможет

slackwarrior ★★★★★
()
Ответ на: комментарий от dimgel

Ну я постгрес просто как пример привел. Просто, как правило, половина тех штук, от которых зависит тестируемый сервис так просто не поднять. Но, короче, мой изначальный посыл был в том, что если даже поднять весь этот зоопарк, подготовить состояние для теста, то упавший тест отлаживать *сильно* сложней чем классический unit-тест. Одно другое не заменяет.

Про мок-не-мок спорить не буду, так как, по мне, это все сильно дискуссионно. Не зря в Гугле пришли к тому, что тесты есть просто малые, средние и большие.

urxvt ★★★★★
()
Ответ на: комментарий от urxvt

Просто, как правило, половина тех штук, от которых зависит тестируемый сервис так просто не поднять.

С этим в принципе согласен, процитированный мною пост с RSDN об этом же. Но на практике кроме скажем отправки писем и интеграции со сторонними системами (1С там какой-нибудь, налоговая и т.п.) лично у меня таких внешних сервисов не было.

Если у тебя ситуация другая, то могу предположить как вариант, что ты вляпался в моднявую какашку под названием «микросервисная архитектура». :) Хотя и здесь обычно можно это всё как-то разрулить, разве что используемая инфраструктура совсем калечная (JEE какой-нибудь); пусть даже дюже гиморно, всё равно лучше чем моки.

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

Хехе, а вот и пригодилась ссылка на того же чела из другого срача: тыц (можно читать подветку начиная с Re[4]; а вот откуда растут ноги у исходного вопроса про тест метода String.length: тыц).

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

Это да, сейчас без 100500 сервисов никуда, мы же все убийц Фейбука делаем. :) Я на жабе пишу, а ты на чем? На C++, как я себе представляю, обычно сильно автономней вещи пишут.

urxvt ★★★★★
()
Ответ на: комментарий от urxvt

На работе тоже жава; иначе с чего бы я стал @DataProvider и JEE упоминать. :)

dimgel ★★★★★
()
Ответ на: комментарий от vikrom

Ну как зачем, вдруг там на выходе 3?

Но там его никогда не будет и мы возвращаемся к тому с чего начали.

crutch_master ★★★★★
()
Ответ на: комментарий от byko3y

Понятно, что просто так не уйти, если ещё не найдены иные способы заработка. Моя проблема в том, что мне разработка перестала приносить удовольствие совсем, хоть по работе, хоть в качестве хобби. Надеюсь, после некоторой передышки, станет снова интересно в кач-ве хобби.

evgeny_aa ★★☆
()
Ответ на: комментарий от evgeny_aa

Моя проблема в том, что мне разработка перестала приносить удовольствие совсем, хоть по работе, хоть в качестве хобби

Ну зато ты теперь понял, как школа, институт, а потом и работа превращает человека в безвольный скот. Я за свою жизнь фул тайм в офисе от силы 5 месяцев работал, и я нисколько не жалею о том, что больше туда не возвращался. В частности, возможность работать когда захочу оставила во мне некую радость от программирования. Я вообще не могу понять людей, которым нравится сидеть 8 часов на работе, чтобы 3 из них работать, а остальные 5 болтать, зырить смешные картинки, болтать в чатиках, и прочее. Я когда сначала пришел на работу, то за 2 месяца сделал модуль, на который планировалось выделить половину человекогода. Я думал, что потом будет проще, потом задания будут сложные, но менее трудоемкими. Потом я понял, что я идиот, что от меня требуется либо выполнять много тупой работы, либо брать плетку в руки и гнать вперед бездарных кодеров - второе можно назвать продвижением по карьерной лестнице.

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

byko3y ★★★★
()
Ответ на: комментарий от filosofia

Это звучит так, словно у вас более половины кода была из геттеров-сеттеров.

После покрытия (почти 100%) действительно важных участков кода для достижения 50% по «пакету» приходилось в том числе и геттеры и сеттеры автотестировать. Как их исключать из этого отчёта (внутри пакета) - не знаю. Мелких подробностей не вспомню - извините. Лет 9 уже прошло :)

Qasta
()
Ответ на: комментарий от no-such-file

У тебя есть два стула - чинить ломающиеся тесты, или чинить ломающийся прод.

Формулировка так себе. Чинить ломающийся прод всё равно придётся. Разница в том, насколько часто и критично.

Deleted
()
Ответ на: комментарий от slovazap

Ещё раз, такое возможно только у абсолютно безграмотных команд, и это ещё надо сильно постараться.

То есть это означает, в реальном мире – у большинства команд…

Deleted
()
Ответ на: комментарий от vikrom

Объясните парадокс) Тесты пишутся после кода и в течение своего жизненного цикла ни разу не детектят ошибки

Как вариант - тестируются слишком простые юниты, аля def fun(a, b): return a + b; на мой взгляд, имеет смысл проверять более менее сложные алгоритмы и композиции ф-ций. Маленькие и простые ф-ции можно вообще прогонять через repl, а тестировать уже их композит, если результат очевиден (или известен) от входных параметров. Пишется хороший многофакторный тест, и *если* он фейлится, тогда уже можно покрыть тестами его составляющие.

(повторюсь, 99% из тех, что я видел, именно такие), но требуют поддержки.

Похоже на овертестинг.

alienclaster ★★★
()
Ответ на: комментарий от vikrom

Кроме того, тесты могут выступать входной точкой при изучении кода.

Я несколько раз о таком читал, но пока не встретил человека, кто действительно бы так делал.

Если в проекте есть тесты - это второе, что я, например, читаю после сигнатуры и беглого взгляда на саму ф-цию / класс. Это формирует хоть какое-то представление о допустимых, крайних, особых случаях входных параметров и что мы там должны получать в итоге - самый быстрый способ, если не используется система контрактов. Когда есть контракты - юнит тесты уже непосредственно проверяют функциональность как блекбокс, на корректную работу алгоритма, и в таком случае в их чтении уже нет особого смысла.

alienclaster ★★★
()
Ответ на: комментарий от evgeny_aa

Я в ближайший год уйду из писательства кода за деньги, потому что эта сфера - сплошной маразм

А где без маразма, на стройке?

alienclaster ★★★
()
Ответ на: комментарий от alienclaster

А где без маразма, на стройке?

Там его ровно столько же. Инфа 100%.

urxvt ★★★★★
()
Ответ на: комментарий от alienclaster

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

filosofia
()

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

Как минимум набор так называемых smoke-тестов интеграционных сразу пишу обязательно. И уже эти тесты делают юниты в большинстве мест ненужными, так как 60-80% кода становится сразу покрыто интеграционными тестами.

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

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

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

nguseff
()

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

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

Хороши тесты библиотек, когда ты можешь вычленить какое-то апи на границе подсистем.

Юнит-тесты твоих классов, как ты правильно понимаешь, не просто неполезны, а ещё и вредны.

max_lapshin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.