Что хуже, любой прописанный mock тоже создаёт новый контекст. Подобный дизайн приводит к тому, что многие делают общий класс для всех интеграционных тестов, который содержит всевозможные моки, и потом наследуются от него.
Это хрестоматийный пример, наряду с калькулятором. Валидация -- хорошо изолируемая функциональная единица, которая полностью инкапсулирует всю логику. Да, юнит тесты позволят сделать более полное покрытие, нежели e2e. Проблемы начинаются, когда логика компонента содержит внешние зависимости, поведение которых придется мокать. Например, в случае моканья базы данных, зачастую часть логики бывает выполнена в компоненте, а другая в самом SQL, и оба представляют собой единую функциональную единицу. Поэтому такой тест с моком базы становится бесполезным.
Это все сферический конь в ваккууме. Как это бывает реально:
Вы тестируете компонент. DI вам говорит, что компонент завязан на entity репозиторий базы данных. И вы здраво хотите его мокнуть. В репозитории стопицот queries. Нужно ли их всех мокать? Нет. В итоге вы судорожно пытаетесь найти какие из них реально используются компонентом, чтобы отмокать только их. Потом замечаете, что некоторые findByXXX() репозитория параметризированы, и пытаетесь разобраться в SQL логике, чтобы точно определить что же делает база. Затем в тесте пытаетесь симулировать какой-то сценарий. В итоге получается 'given' простыня из предусловий и моков, понятная только вам, здесь и сейчас. Ни один читающий после вас эту лабуду не поймет на лету почему findByXXX() должен возвратить именно 42. После многочисленных попыток отдебажить сценарий и установить правильное поведение моков, вы, наконец, получаете зеленый тест, запускате приложение и... бум! ничего не работает. Ошиблись в моке, не учли поведние бд, ретроградный меркурий, еще что-нибудь.
На следующей неделе компоненту вместо findByXXX() нужно использовать более продвинутыйfindByYYY(). Одна строчка изменений в бизнес коде и двести рутинных поправок в тестовых моках.
Пирамида тестирования не указывает, что такое единица поведения и где провести адекватную границу. Некоторые следуют правилу один класс -- один тест. Некоторые тестируют геттеры-сеттеры, тогда как другие -- полностью загруженное приложение. Третьи -- делают "срезы" вообще по принципу быстро (юнит) -- медленно (интеграционный) и тупо мокают базу данных. Поэтому если есть возможность выделить и изолировать функциональную единицу как модуль, то здесь и должен проходить уровень тестов.
Если же вы работаете по классическому TDD, то вы как правило пишите тест для каждого из компонентов вне зависимости от функционала и уровня, ибо для тестов уровня модуля будет слишком большая итерация. Отсюда берется многократное покрытие.
Не существует простого и чистого и понятного кода.
Если ваша команда не может генерировать простой и понятный код, или же вы используете, средства разработки, которые не способствуют простой и понятной огранизации кода и помимо бизнес логики привносят стороннюю когнитивную нагрузку, то боюсь, никакое TDD вам здесь не поможет. Весь ваш проект с самого начала -- это мусор.
Чистый код, это сотни итераций рефакторинга, которые и создают в итоге элегантность и чистоту.
Как правило за рефакторинг никто вам платить не собирается. Продакт всегда требует новых фич. Если команда изначально создает технический долг, в надежде, когда придут хорошие времена и можно будет его спокойно отрефакторить, то они ошибаются -- такие времена никогда не настанут. Проще будет переписать весь продукт с нуля.
Они документируют то, что ожидается от кода. Это и есть контракт вашего компонента,
Полная херь (не в обиду вам). Документирует документация. Контракт определяется дизайном, и такими средствами как WSDL, OpenAPI, UML, etc. А вот тесты могут проверить соответвтвие имплементации дизайну. Ибо никому в здравом уме не впилось разбираться с вашими whenSomeShitHappend_thenShouldReturnXXX() , написанными на кривом языке с кучей непонятных предусловий. Есть конечно BDD и жесткая спека, когда вы пишите софт для NASA, но это, думаю, не ваш случай.
Потому что если вам репортирует клиент - он может уже быть мертвым в некоторых ситуациях. А в других - его бизнес будет мертв.
Тем не менее, все конторы тестируют на хомячках. А по поводу бизнеса и к великому сожалению даже жизни, поймите одно простое правило -- всем пофиг. Защита бизнеса клиента -- дело рук самого клиента (SLA, fallback, etc.). В большинстве же случаев клиентский сектор уже настолько приспособился к случайным сбям и ошибкам, что это перестало быть проблемой.
А вот проекты у которых 0% покрытия и 5 лет разработки, после которой кодер стал архитектором - в них вот никогда ничего не рефакторят.
Не совсем понимаю почему 0% покрытия? Я говорил о функциональных тестах, которые тестируют лишь заявленный функционал, а не внутреннюю кухню. Например, API тесты микросервиса. Для последующего рефакторинга это как раз идеально -- вы смело можете хоть поменять базу данных, хоть перелопатить весь внутренний код, не боясь, что это затронет консьюмеров.
Изменение же поведения 1 компонента не приводит к развалу всего стека, только самого компонента
Это наивное идеализированное представление о реальной разработке. Уверяю вас, что уже на следующей итерации вы получите новое бизнес-требование, причем тривиальное, которое как серпом по яйцам пройдется по всей вашей стройной архитектуре, требуя перелопатить большинство контрактов, начиная от API и кончая базой. И вместе с этим еще стопицот бесполезных тестов.
Да, я понимаю. Но в этом и состоит основная проблема. Сначала вы пишите мелкие компоненты, тестируете каждый из них. Затем тестируете из связку. Затем добираетесь до функционального уровня, например API и тестируете уже его. В итоге функционал одного и того же компонента покрывается многократно, что затрудняет будущий рефакторинг и вообще любые изменения. При всем при том, что ваш внутренний дизайн никому особо не упал, важно, чтобы был соблюдён контракт на уровне API.
Если система обладает новым свойством, несводимым к её частям, то откуда взялось это свойство?
Поясню за эмерджентность, ибо многие ее понимают неправильно.
Во-первых, классикой эмерджентной величины в физике является центробежная сила. Ее используют технари для расчета прочности конструкций. Проявляет себя в реальном мире, но как физической сущности ее не существует. Обоснование ее не так тривиально: ее не возможно объяснить составными частями, для этого нужно понимать что такое неинерциальные системы отсчета. В квантовой теории поля есть куча эмерджентных сущностей (если вообще не все), начиная от виртуальных частиц и процедуры перенормировки. То есть для объяснения данного эмерджентного свойства не всегда требуется разложить систему на части, сколько сменить систему отсчета.
Строгая логическая формализация эмерджентности всегда приводит к противоречиям по типу 2+2=5
Это основа ошибки, т.к. кто-то когда-то сказал, что целое есть сумма частей. Целое не сумма частей, а произведение оных. Новые свойства системы возникают из совокупности уникальных состояних ее частей. Например, возьмем комнату с лампочкой и выключателем. У комнаты будет два состояния -- освещена и не освещена, в зависимости от того, горит лампочка или нет. Теперь добавим в комнату еще одну лампочку с выключателем. Теперь у нас появилось 3 состояния: не освещена (обе лампочки не горят), освещена (горят обе лампочки) и полуосвещена (горит одна лампочка). А также родилось новое эмерджентное свойство: яркость, которое будет все более присутствовать при дальнейшем добавлении лампочек. Причем, два собственных состояния системы (горит только первая или только вторая лампочка) взаимокоммутируемы и дают только одно новое состояние системы.
В этом состоит проблема редукционистского подхода в науке (разобрать систему по частям и изучать их по отдельности): не учитываются связи между компонентами, которые в некоторых сложных системах играют ключевую роль. А чрезвычайно сложные системы как мозг и нейросети вообще не позволяют локазизовать и выделить отдельные компоненты.
Хотите немного мистики? Каждый из нас независимо от рода деятельности проводит огромное количество времени в день со своим смартфоном, который уже прочно стал частью нашей жизни. А теперь: кто-нибудь может вспомнить, когда ему снился его смартфон?
Ну вот я как раз имел ввиду не художественные навыки. Даже если попытаться представить в голове абстрактное или даже реальное место, оно у меня представляется серым, абстрактным и без деталей. А вот во сне детализация как раз не условность, а реальный рендер, в чем я убеждался неоднократно. Да, нестабильный, многое меняется по ходу, но детализация поражает. Да, возможно какие-то изначальные абстрактные образы берутся из памяти, но потом дорабатываются головной нейросеткой в реальном времени до полной картины. В сознании такого не происходит.
Ну вот ни разу. Тем более, что вы сами себя опровергли в п10. TDD к проектированию имеет такое же отношение как автомастерская к автопроизводителю. Простой, чистый и понятный код -- это и есть способ проектирования.
Реальность: TDD экономит время на отладке и переосмыслении.
TDD обнаруживает часть ошибок до того, как они будут репортированы клиентом. Тесты не инвестиция, а мусор, кодовая база которого может до 10х раз превышать бизнес код. Инвестиция -- функционал и качественный код, к которому TDD не имеет никакого отношения. Говнокодить на ура можно и по TDD.
Код становится проще, понятнее и устойчивее к изменениям. Возникает меньше багов, проще менять требования.
Вот эта основная херь, на которую ведутся миллионы. Все с точностью до наоборот. Мелкие итеративные шаги ведут к многократному покрытию одного фрагмента. Поэтому минимальное изменение требований функционала валит стопицот тестов, ковырять которые приходится долго и рутинно. Поэтому любой рефакторинг вообще кладется в долгий ящик, т.к. никому не уперлось возиться мегабайтами свалившихся тестов, при всем при том, что это лишь внутреннее архитектурное изменение, которое не трогает заявленный функционал.
Реальность: Маленькие шаги позволяют проектировать поведение.
Для того, чтобы построить небоскреб, нужно сложить кирпичи в стену и проверить ее на прочность. В следующей итерации подумаем что с этим делать. Проектирование поведения происходит в бизнес коде. Тесты -- для фиксирования поведения и проверки корректности (соответствии поведения заявленному контракту). А уж никак не для проектирования.
Вывод: пишите функциональные тесты, которые проверяют внешнее поведение (API, спецификацию, т.д.). Избегайте многократного покрытия. Проектируйте без TDD.
Реальная история. В студенческие годы дописывал курсовой проект. Как всегда все делал в последнюю ночь, завтра сдавать. Прокопошился до четырех утра, прогнал пару раз -- вроде работает. Лег спать на пару часов. Во сне внезапно увидел кусок своего кода и явный баг, где условие было инвертировано. На утро проснулся, посмотрел в проект -- так и было. Быстро исправил пересобрал проект. В итоге все приняли.
А вообще, если такое происходит периодически -- это плохой знак о том что мозг занят дневными задачами и практически не отдыхает.
Периодически "выпадаю" в осознанный сон, где я путешествую по различного рода локациям. Каждый раз у меня возникает один и тот же вопрос: каким образом мой мозг в реальном времени генерирует такую сложную и детализированную структуру? Если это город, то откуда берется столь сложное устройство улиц, архитектура зданий, детали, орнаменты и даже текстура стен? Специально многократно фокуссировался на структурах, и поражался их сложности и детализированности. При всем при том, что рисую я из рук вон плохо, и не могу на бумаге воспроизвести ничего из увиденного. Жаль, осознанный сон длится недолго: как правило меня либо "выбрасывает" из сна, либо наоборот "углубляюсь", где структуры становятся более абстрактными, а локация иррациональной.
Если бизнес код все ещё понятен и юзабелен, то поддерживать снежный ком и месиво из тупых и бесполезных юнит тестов уже не представляется возможным. Код не эволюционирует, рефакторинг не производится, новые фичи прикручиваются сбоку, либо вообще игнорятся. Проект а станции, зато метрики покрытия зелёные, QA счастлив!
Никто не умеет писать хороший код. Есть те, кто думают, что пишут хороший код потому что им так сказали в книжке или на презентации люди, которые уже 20 лет ничего не писали.
Тут особо не о чем рассказывать. Base -- все ещё достаточно сильная гарантия, которая обеспечивает частичную консистентность в некоем будущем. Большинство систем вообще никак не гарантируют консистентность данных, ни в настоящем, ни в будущем, и обеспечивают ее только в случае happy path при отсутствии race condition. Благодаря крайне низкой вероятности подобных сбоев, а также налаженной пользовательской поддержке, данные системы имеют быть место.
Любые обязательные метрики, без учёта контекста - это бред.
Благо в этом мире бреда хватает на всех.
Написание теста на свой код - это моментальный сигнал о том, хороший ли у тебя код
Да всем ровным счётом по барабану какой там у вас код и как там он устроен. В проекте важны дедлайны и соответствует ли функционал заявленной бизнес спецификации или нет. Именно это и нужно тестировать. При наличии тестового покрытия функционала, его потом можно отрефакторить без лишнего геморроя.
И как я уже сказал выше, юниты отнюдь не являются метрикой качества кода. В дизайне появляется излишняя фрагментарность, на порядки ухудшающая читабельность, нарушается инкапсуляция и области видимости. С зависимостями все ещё хуже: мокать repository -- глупое и неблагодарное занятие.
Аргументы типа "если вы не любите кошек, то вы не умеете их готовить" -- это фактически единственный аргумент адептов свидетелей ТДД. При всем при том есть стоптцот теоретических изысканий, методик и книг о том, как правильно писать чистый и понятный код. Однако нет ни одной удобоваримой теории как правильно писать тесты. Вся "теория" ограничивается сферическим конем в вакууме ввиде конкретного примера калькулятора, и пачкой наивных абстрактных наставнических соображений, зачастую противоречащих друг другу.
Почему в реальном мире это не работает, неплохо пояснил коллега постом ниже. Из своей практики замечу, что при внедрении метрики обязательного покрытия в 80% разработка проекта замедлилась в 3 раза, кодовая база распухла, а читабельность ухудшилась благодаря излишней фрагментарности. Команда напрочь отказывалась производить всякий рефакторинг из-за сопутствующих проблем с тестами. И главное, это практически никак не повлияло на финальные метрики ошибок.
Страшно трогать, когда на при малейшем тривиальнейшем изменении бизнес логики валятся стоптцот абсолютно бесполезных юнит тестов. Или когда логика мокирования становится в десятки раз сложнее самого функционала, и уже сами тесты являются основными источником ошибок.
Согласен со всем сказанным, кроме пункта 4. И здесь виновата сама архитектура спринга, ибо никогда время запуска приложения не считалось критичным. Разработчики подождут -- им за это платят. В итоге вся эта белебердень с юнитами, слайсами, ленивой инициализацией была придумана, чтобы хоть как-то ускорить тесты. Поэтому если разработчики самого спринга рекомендуют вам писать юниты без контекста где это только возможно, значит они где-то крупно прокололись.
Что хуже, любой прописанный mock тоже создаёт новый контекст. Подобный дизайн приводит к тому, что многие делают общий класс для всех интеграционных тестов, который содержит всевозможные моки, и потом наследуются от него.
Это хрестоматийный пример, наряду с калькулятором. Валидация -- хорошо изолируемая функциональная единица, которая полностью инкапсулирует всю логику. Да, юнит тесты позволят сделать более полное покрытие, нежели e2e. Проблемы начинаются, когда логика компонента содержит внешние зависимости, поведение которых придется мокать. Например, в случае моканья базы данных, зачастую часть логики бывает выполнена в компоненте, а другая в самом SQL, и оба представляют собой единую функциональную единицу. Поэтому такой тест с моком базы становится бесполезным.
Это все сферический конь в ваккууме. Как это бывает реально:
Вы тестируете компонент. DI вам говорит, что компонент завязан на entity репозиторий базы данных. И вы здраво хотите его мокнуть. В репозитории стопицот queries. Нужно ли их всех мокать? Нет. В итоге вы судорожно пытаетесь найти какие из них реально используются компонентом, чтобы отмокать только их. Потом замечаете, что некоторые
findByXXX()
репозитория параметризированы, и пытаетесь разобраться в SQL логике, чтобы точно определить что же делает база. Затем в тесте пытаетесь симулировать какой-то сценарий. В итоге получается 'given' простыня из предусловий и моков, понятная только вам, здесь и сейчас. Ни один читающий после вас эту лабуду не поймет на лету почемуfindByXXX()
должен возвратить именно 42. После многочисленных попыток отдебажить сценарий и установить правильное поведение моков, вы, наконец, получаете зеленый тест, запускате приложение и... бум! ничего не работает. Ошиблись в моке, не учли поведние бд, ретроградный меркурий, еще что-нибудь.На следующей неделе компоненту вместо
findByXXX()
нужно использовать более продвинутыйfindByYYY()
. Одна строчка изменений в бизнес коде и двести рутинных поправок в тестовых моках.Пирамида тестирования не указывает, что такое единица поведения и где провести адекватную границу. Некоторые следуют правилу один класс -- один тест. Некоторые тестируют геттеры-сеттеры, тогда как другие -- полностью загруженное приложение. Третьи -- делают "срезы" вообще по принципу быстро (юнит) -- медленно (интеграционный) и тупо мокают базу данных. Поэтому если есть возможность выделить и изолировать функциональную единицу как модуль, то здесь и должен проходить уровень тестов.
Если же вы работаете по классическому TDD, то вы как правило пишите тест для каждого из компонентов вне зависимости от функционала и уровня, ибо для тестов уровня модуля будет слишком большая итерация. Отсюда берется многократное покрытие.
Если ваша команда не может генерировать простой и понятный код, или же вы используете, средства разработки, которые не способствуют простой и понятной огранизации кода и помимо бизнес логики привносят стороннюю когнитивную нагрузку, то боюсь, никакое TDD вам здесь не поможет. Весь ваш проект с самого начала -- это мусор.
Как правило за рефакторинг никто вам платить не собирается. Продакт всегда требует новых фич. Если команда изначально создает технический долг, в надежде, когда придут хорошие времена и можно будет его спокойно отрефакторить, то они ошибаются -- такие времена никогда не настанут. Проще будет переписать весь продукт с нуля.
Полная херь (не в обиду вам). Документирует документация. Контракт определяется дизайном, и такими средствами как WSDL, OpenAPI, UML, etc. А вот тесты могут проверить соответвтвие имплементации дизайну. Ибо никому в здравом уме не впилось разбираться с вашими
whenSomeShitHappend_thenShouldReturnXXX()
, написанными на кривом языке с кучей непонятных предусловий. Есть конечно BDD и жесткая спека, когда вы пишите софт для NASA, но это, думаю, не ваш случай.Тем не менее, все конторы тестируют на хомячках. А по поводу бизнеса и к великому сожалению даже жизни, поймите одно простое правило -- всем пофиг. Защита бизнеса клиента -- дело рук самого клиента (SLA, fallback, etc.). В большинстве же случаев клиентский сектор уже настолько приспособился к случайным сбям и ошибкам, что это перестало быть проблемой.
Не совсем понимаю почему 0% покрытия? Я говорил о функциональных тестах, которые тестируют лишь заявленный функционал, а не внутреннюю кухню. Например, API тесты микросервиса. Для последующего рефакторинга это как раз идеально -- вы смело можете хоть поменять базу данных, хоть перелопатить весь внутренний код, не боясь, что это затронет консьюмеров.
Это наивное идеализированное представление о реальной разработке. Уверяю вас, что уже на следующей итерации вы получите новое бизнес-требование, причем тривиальное, которое как серпом по яйцам пройдется по всей вашей стройной архитектуре, требуя перелопатить большинство контрактов, начиная от API и кончая базой. И вместе с этим еще стопицот бесполезных тестов.
То есть 1 байт -- это 256. 2 байта -- это 65536, а не 512.
Да, я понимаю. Но в этом и состоит основная проблема. Сначала вы пишите мелкие компоненты, тестируете каждый из них. Затем тестируете из связку. Затем добираетесь до функционального уровня, например API и тестируете уже его. В итоге функционал одного и того же компонента покрывается многократно, что затрудняет будущий рефакторинг и вообще любые изменения. При всем при том, что ваш внутренний дизайн никому особо не упал, важно, чтобы был соблюдён контракт на уровне API.
Поясню за эмерджентность, ибо многие ее понимают неправильно.
Во-первых, классикой эмерджентной величины в физике является центробежная сила. Ее используют технари для расчета прочности конструкций. Проявляет себя в реальном мире, но как физической сущности ее не существует. Обоснование ее не так тривиально: ее не возможно объяснить составными частями, для этого нужно понимать что такое неинерциальные системы отсчета. В квантовой теории поля есть куча эмерджентных сущностей (если вообще не все), начиная от виртуальных частиц и процедуры перенормировки. То есть для объяснения данного эмерджентного свойства не всегда требуется разложить систему на части, сколько сменить систему отсчета.
Это основа ошибки, т.к. кто-то когда-то сказал, что целое есть сумма частей. Целое не сумма частей, а произведение оных. Новые свойства системы возникают из совокупности уникальных состояних ее частей. Например, возьмем комнату с лампочкой и выключателем. У комнаты будет два состояния -- освещена и не освещена, в зависимости от того, горит лампочка или нет. Теперь добавим в комнату еще одну лампочку с выключателем. Теперь у нас появилось 3 состояния: не освещена (обе лампочки не горят), освещена (горят обе лампочки) и полуосвещена (горит одна лампочка). А также родилось новое эмерджентное свойство: яркость, которое будет все более присутствовать при дальнейшем добавлении лампочек. Причем, два собственных состояния системы (горит только первая или только вторая лампочка) взаимокоммутируемы и дают только одно новое состояние системы.
В этом состоит проблема редукционистского подхода в науке (разобрать систему по частям и изучать их по отдельности): не учитываются связи между компонентами, которые в некоторых сложных системах играют ключевую роль. А чрезвычайно сложные системы как мозг и нейросети вообще не позволяют локазизовать и выделить отдельные компоненты.
Хотите немного мистики? Каждый из нас независимо от рода деятельности проводит огромное количество времени в день со своим смартфоном, который уже прочно стал частью нашей жизни. А теперь: кто-нибудь может вспомнить, когда ему снился его смартфон?
Ну вот я как раз имел ввиду не художественные навыки. Даже если попытаться представить в голове абстрактное или даже реальное место, оно у меня представляется серым, абстрактным и без деталей. А вот во сне детализация как раз не условность, а реальный рендер, в чем я убеждался неоднократно. Да, нестабильный, многое меняется по ходу, но детализация поражает. Да, возможно какие-то изначальные абстрактные образы берутся из памяти, но потом дорабатываются головной нейросеткой в реальном времени до полной картины. В сознании такого не происходит.
Ну вот ни разу. Тем более, что вы сами себя опровергли в п10. TDD к проектированию имеет такое же отношение как автомастерская к автопроизводителю. Простой, чистый и понятный код -- это и есть способ проектирования.
TDD обнаруживает часть ошибок до того, как они будут репортированы клиентом. Тесты не инвестиция, а мусор, кодовая база которого может до 10х раз превышать бизнес код. Инвестиция -- функционал и качественный код, к которому TDD не имеет никакого отношения. Говнокодить на ура можно и по TDD.
Вот эта основная херь, на которую ведутся миллионы. Все с точностью до наоборот. Мелкие итеративные шаги ведут к многократному покрытию одного фрагмента. Поэтому минимальное изменение требований функционала валит стопицот тестов, ковырять которые приходится долго и рутинно. Поэтому любой рефакторинг вообще кладется в долгий ящик, т.к. никому не уперлось возиться мегабайтами свалившихся тестов, при всем при том, что это лишь внутреннее архитектурное изменение, которое не трогает заявленный функционал.
Для того, чтобы построить небоскреб, нужно сложить кирпичи в стену и проверить ее на прочность. В следующей итерации подумаем что с этим делать. Проектирование поведения происходит в бизнес коде. Тесты -- для фиксирования поведения и проверки корректности (соответствии поведения заявленному контракту). А уж никак не для проектирования.
Вывод: пишите функциональные тесты, которые проверяют внешнее поведение (API, спецификацию, т.д.). Избегайте многократного покрытия. Проектируйте без TDD.
Реальная история. В студенческие годы дописывал курсовой проект. Как всегда все делал в последнюю ночь, завтра сдавать. Прокопошился до четырех утра, прогнал пару раз -- вроде работает. Лег спать на пару часов. Во сне внезапно увидел кусок своего кода и явный баг, где условие было инвертировано. На утро проснулся, посмотрел в проект -- так и было. Быстро исправил пересобрал проект. В итоге все приняли.
А вообще, если такое происходит периодически -- это плохой знак о том что мозг занят дневными задачами и практически не отдыхает.
Периодически "выпадаю" в осознанный сон, где я путешествую по различного рода локациям. Каждый раз у меня возникает один и тот же вопрос: каким образом мой мозг в реальном времени генерирует такую сложную и детализированную структуру? Если это город, то откуда берется столь сложное устройство улиц, архитектура зданий, детали, орнаменты и даже текстура стен? Специально многократно фокуссировался на структурах, и поражался их сложности и детализированности. При всем при том, что рисую я из рук вон плохо, и не могу на бумаге воспроизвести ничего из увиденного.
Жаль, осознанный сон длится недолго: как правило меня либо "выбрасывает" из сна, либо наоборот "углубляюсь", где структуры становятся более абстрактными, а локация иррациональной.
На старой флешке раздел создать, чтобы USB2 только было.
Если бизнес код все ещё понятен и юзабелен, то поддерживать снежный ком и месиво из тупых и бесполезных юнит тестов уже не представляется возможным. Код не эволюционирует, рефакторинг не производится, новые фичи прикручиваются сбоку, либо вообще игнорятся. Проект а станции, зато метрики покрытия зелёные, QA счастлив!
Никто не умеет писать хороший код. Есть те, кто думают, что пишут хороший код потому что им так сказали в книжке или на презентации люди, которые уже 20 лет ничего не писали.
Тут особо не о чем рассказывать. Base -- все ещё достаточно сильная гарантия, которая обеспечивает частичную консистентность в некоем будущем. Большинство систем вообще никак не гарантируют консистентность данных, ни в настоящем, ни в будущем, и обеспечивают ее только в случае happy path при отсутствии race condition. Благодаря крайне низкой вероятности подобных сбоев, а также налаженной пользовательской поддержке, данные системы имеют быть место.
Благо в этом мире бреда хватает на всех.
Да всем ровным счётом по барабану какой там у вас код и как там он устроен. В проекте важны дедлайны и соответствует ли функционал заявленной бизнес спецификации или нет. Именно это и нужно тестировать. При наличии тестового покрытия функционала, его потом можно отрефакторить без лишнего геморроя.
И как я уже сказал выше, юниты отнюдь не являются метрикой качества кода. В дизайне появляется излишняя фрагментарность, на порядки ухудшающая читабельность, нарушается инкапсуляция и области видимости. С зависимостями все ещё хуже: мокать repository -- глупое и неблагодарное занятие.
Аргументы типа "если вы не любите кошек, то вы не умеете их готовить" -- это фактически единственный аргумент адептов свидетелей ТДД. При всем при том есть стоптцот теоретических изысканий, методик и книг о том, как правильно писать чистый и понятный код. Однако нет ни одной удобоваримой теории как правильно писать тесты. Вся "теория" ограничивается сферическим конем в вакууме ввиде конкретного примера калькулятора, и пачкой наивных абстрактных наставнических соображений, зачастую противоречащих друг другу.
Почему в реальном мире это не работает, неплохо пояснил коллега постом ниже. Из своей практики замечу, что при внедрении метрики обязательного покрытия в 80% разработка проекта замедлилась в 3 раза, кодовая база распухла, а читабельность ухудшилась благодаря излишней фрагментарности. Команда напрочь отказывалась производить всякий рефакторинг из-за сопутствующих проблем с тестами. И главное, это практически никак не повлияло на финальные метрики ошибок.
Страшно трогать, когда на при малейшем тривиальнейшем изменении бизнес логики валятся стоптцот абсолютно бесполезных юнит тестов. Или когда логика мокирования становится в десятки раз сложнее самого функционала, и уже сами тесты являются основными источником ошибок.
Согласен со всем сказанным, кроме пункта 4. И здесь виновата сама архитектура спринга, ибо никогда время запуска приложения не считалось критичным. Разработчики подождут -- им за это платят. В итоге вся эта белебердень с юнитами, слайсами, ленивой инициализацией была придумана, чтобы хоть как-то ускорить тесты. Поэтому если разработчики самого спринга рекомендуют вам писать юниты без контекста где это только возможно, значит они где-то крупно прокололись.