Автоматизация тестирования
У нас сейчас есть актуальный кейс - проблема с БД. Создаем тикет у оракла и они (оракл) требуют Verbose лог с проблемой. Генерим лог, отправляем в оракл на анализ.
Собственно говоря, не просто так придуманы 1) уровни логгирования (error, warning, info, debug и еще куча кастомных уровней) и 2) не просто так придуманы циклические логи, сроки давности логов и ограничения по размеру логов.
Все это работает на то, чтобы получать нужную для исследования проблемы информацию. И да, если кто-то вместо уровня debug использует info, то он сам себе злобный буратино :)
Это я всё понимаю. Не понимаю, стоит ли действительно писать так:
лог
валидатор входящих
лог
перформанс каунтер
лог
строчка кода
лог
валидатор предыдущей строчки кода
лог
ветвистая обработка исключений - по логу на каждую обработку
На одну строчку кода 20 строчек обслуги, не считая скобок. Функция занимает экран-полтора, хотя делают по сути одну операцию. И так в почти каждой функции. А функций может быть 10-20 друг друга вызывать. И когда знакомишься с таким кодом, долго думаешь, что тут происходит. И так трудно докопаться до сути, продравшись через дебри паттернов, так ещё у себя в голове и очистить всё от обслуживающей логики нужно.
Может, проще дамп памяти скинуть? Там как раз весь контекст. А читать все эти verbose - кто их будет?
Тоже мне бином Ньютона. Смортря чей компилятор. Микрософтовский, значит микрософт.
Пришёл менеджер, наорал на поддержку "почему на каждый инцидент в среднем 25 часов тратится", ему сказали что повторить ошибку без данных сложно. Он побежал к разработчикам и потребовал возможность записывать каждый чих. Вот тебе и записи в лог где можно и где нельзя.
Может, проще дамп памяти скинуть?
Дамп памяти надо еще уметь анализировать. И нет, его скинуть не проще. Т.к. дамп - штука крупная и на каждую ошибку генерировать по дампу - очень накладно.
А читать все эти verbose - кто их будет?
Их будет читать тот, кто ищет проблему. Иногда достаточно исключения с колл-стэком, а может оказаться и так, что надо понять процессы задолго до вызова функции, которая привела к исключению.
Дамп памяти надо еще уметь анализировать.
Я думал, это такая штука, которую в какой-нибудь Студии открыл, а она тебе рраз - и открылся код, где возникает ошибка, с остановом на нужной строчке, и все данные в контексте всего стека вызовов, и по стеку гулять можно. Что, нету такого? А было бы удобно... Для суперсложных распределённых систем этого может быть недостаточно, но для основного большинства ошибок - вполне.
Логирование, это прямо какая-то чуждая кодированию вещь, требующая дофига обслуги, настроек и засоряющая код. Это как меню в ресторане вместо выбора самих блюд. Если бы были блюда готовые и можно выбрать из реальных, а не нарисованных, то это лучший вариант, чем листать всякие книжки с картинками. Так и в багфиксинге - если можно саму память и состояние приложения задампить со всем стеком вызовов, получая ситуацию ошибки точно такую же, как возникла у клиента, то все эти логи нафиг не нужны. В них самих ошибок можно наделать дофига.
Вот тебе и записи в лог где можно и где нельзя.
------
Ндаа... и это вместо того, чтобы корректно проимплементить компоненты компилятора...
Но лучший вариант все же был у меня.
У нас тогда пошли массовые проблемы с подключением к Ораклу... что-то работало, что-то отваливалось...
Обвешал коннект тру-катчами с оракловской спецификой... логи и трекинг... причину - не выяснили, но работать - заставил.
А потом был переход на постгрее и смена команды разработки... оставившей всю обработку оракловских ехсептионов...
Вопрос. А есть ли смысл в таких логах? Кто потом эти терабайты читает и разбирает?
Мальчик, или в школу. Там тебя научат делать ELK стек и анализировать логи с помощью ChatGPT
Зы. Ты случайно не писал систему логирования для ActiveMQ ? А то там ничего не логируется.
Логирование, это прямо какая-то чуждая кодированию вещь, требующая дофига обслуги, настроек и засоряющая код
Мальчик, тебе нужно в ясельную группу. На горшок.
Зы. Ты в курсе, что такое разделение ответственности в коже (code separation) ?
Выделяй inner helper class вызывай из него логируемый метод и добавляй столько логирования, сколько нужно.
А то развели тут клуб благородных девиц.
Лучше дамп памяти со стеком вызовов и всем контекстом, и остановом в точке возникновения ошибки, чем какие-то тонны текстовых писулек.
Можно разделить как в логировании: уровень warning - небольшой дампчик ближнего окружения; уровень error - дамп побольше, окружение поширше; уровень fatal super-puper apocalypse - полный дамп приложения в RAM. Короче, настроить можно и по мере накопления статистики решить, насколько много дампить. Вобщем, всё как в текстовых логах, только без текстовых логов.
Я вообще поражаюсь, как люди любят занимать себя пустой работой, типа написания логов или тестов в объёме, на порядок превышающем сам код. Вместо того, чтобы придумать, как бы от работы освободиться и заняться любимым делом или предаваться ничегонеделанию. Лень - двигатель прогресса! А от работы и кони дохнут.
Я думал, это такая штука, которую в какой-нибудь Студии открыл, а она тебе рраз - и открылся код, где возникает ошибка, с остановом на нужной строчке, и все данные в контексте всего стека вызовов, и по стеку гулять можно. Что, нету такого? А было бы удобно...
Не то, чтобы такого нет :) Но, 1) создать минидамп - это дорого (это сотни мегабайт) 2) в студии дамп открыть можно, но для анализа дампа студии мало, нужны какие-то хардкорные приложения типа WinDbg 3) дамп без pdb файлов - мусор (ну если только ты не гуру ассемблера :), да и то не факт), а значит надо делать добавлять менеджмент pdb файлов.
Так что ниша дампов очень узкая. Это либо дэдлоки (тогда пользователь сам должен создать дамп) , либо это перехват исключения перед тем, как схлопнется приложение.
Чёт в голове крутится... Вот все говорят, я пишут мол сначала интерфейсы, а потом классы по ним. А не проще наоборот - сначала класс, а интерфейс из него одной командой вытаскивается? А вот наоборот нельзя - максмум по интерфейсу можно заготовку класса сделать.
Иногда бывает, что нет времени всё делать по фен-шую, писать тесты, интерфейсы и прочее - работать надо. А марафет потом как-нибудь наведём. И вот настаёт этот волнительный момент, когда можно расслабиться и пару дней пинать балду на расслабоне дописывать интерфейсы к классам, и тут разработчики IDE тебе подсирают - есть оказывается команда для извлечения интерфейса в пару кнопок. Предатели!
А не проще наоборот - сначала класс, а интерфейс из него одной командой вытаскивается?
Новечку, конечно, проще.
Однако в тот момент, когда ты начнешь писать тестируемый код, ты поймешь, что сначала должны быть интерфейсы :) Плюс ко всему, подход "интерфейсы вперед" позволяет задумываться об архитектуре на ренней фазе.
А марафет потом как-нибудь наведём.
Не наведешь :)
Во-первых, в 95% случаев на рефакторинг нет ни времени, ни бюджета. Т.е. должны быть очень веские причины (функционал, баги) для рефакторинга.
Во-вторых, код типа такого (все на классах)
public void Foo (DbConnection db, UserProvider provider) { UserService srv = new UserService (db); User user = srv.GetUser (provider); if (user != null) { user.Login (); } }
нетестируем и без рефакторинга тут уже ничего не сделаешь.
Всё легко и просто тестируется. Берёшь вместо реальной БД тестовую, которая может даже быть частичной копией реальной, и тестируешь прямо сам класс, а не кучку моков. Заодно и проблемы с тестовыми данными отпадают, которые что с вашими интерфейсами, что без, всё равно нужно откуда-то взять, как-то нагенерить.
Ещё такое мнение читал, что интерфейсы мол для быстрой замены реализаций. Как у вас в 95% случаев нет времени на рефакторинг, так и в 95% случаев никто реализацию потом не меняет. Вот в моём проекте 15-летней давности всё на интерфейсах, а UI как был из старинных времён, так его никто и не заменил. Как и вообще почти всё внутри. И сейчас логику просто переписываем с нуля, стараясь по возможности повторить её на новых версиях фреймворка, языка и вообще всего. А старые интерфейсы просто захламляли код.
Если надо что-то заменить, то обычно сначала отказываются от замены, потом опять отказываются, потом ещё раз отказываются, потом это всё работает уже лет 5-10, а потом просто переписывают. И снова 5-10 лет без замены. Спонтанные подмены СУБД, GUI и прочих вещей - это из феншуйских задвигов. В реальном кровавом энтерпрайзе всем этим на заморачиваются, а просто не трогают, пока оно работает и пока не припрёт. В моём проекте до сих пор бы не трогали, и жило бы всё и 20 лет, и дольше, но IE уже не поддерживается.
Ну во-первых, подсовывать под каждый тест реальную БД - это очень накладно. При этом не забываем, что тесты должны быть атомарными, т.е. независимыми друг от друга. Более или менее эффективно это может работать только с помощью собственного расширения для тестового фреймворка + работающая со снэпшотами БД (оракл или MS) + самописный UnitOfWork, который надо использовать как в расширении тестового фреймворка, так и в продуктивном коде. И даже в этом случае, производительность таких тестов будет желать лучшего. Я уж не говорю о том, что будут не юнит-тесты :D
Во-вторых, приведенный код нестестируем не столько из-за БД, сколько из-за new и из-за того, что UserService - реальный объект, а значит нельза мокнуть функцию GetUser. Которая, в свою очередь, тоже возвращает реальный объект, а значит нельзя проверить была ли вызвана функция Login :)
Короче говоря, использование классов сделали эту функцию нетестируемой юнит-тестами :) Это не значит, что функцию нельзя протестировать, но труда для создания теста надо будет вложить в десятки раз больше. Так что лучше было бы сразу писать на абстракциях :D
А не проще наоборот - сначала класс, а интерфейс из него одной командой вытаскивается?
Мне кажется, в данном случае, проще будет вообще ничего не думать перед написанием программы, а сразу начать ее писать
что нет времени всё делать по фен-шую, писать тесты, интерфейсы и прочее - работать надо.
Так именно правильная работа в этом и заключается - всё делать по фен-шую, писать тесты, интерфейсы и прочее.
Всё, что делается по другому, со временем превращается в большую кучу мусора, как есть актуальный проект.
При этом, даже если делать все по правилам нет гарантии получения отличного кода.
Но переубеждать кого то в этом - бессмыслнная затея.
со временем превращается в большую кучу мусора
------
Оно всегда превращается в большую кучу мусора - по одной методике - раньше, по другой - позднее.
Главное в проекте - успеть спрыгнуть до того как выяснится что большая куча мусора уже образовалась.