Здравствуйте, ·, Вы писали:
P>>Чего стесняться — я вам прямо говорю. Переносим код связывания из размытого dependency injection в контроллер
·>Именно. И по итогу получается, что издержек у тебя больше, строчек кода добавляется огромная куча.
Нету никакой огромной кучи
P>>Это я вам пример привожу, что функциональность приложения и функции в джаве это две большие разницы.
·>А теперь тебе осталось продемонстировать, что я когда либо утверждал что это одно и то же, или опять софистикой занимаешься? Я лишь говорил, что функциональность можно выражать в виде функций, в т.ч. и ЧПФ. Вот в твоём привёдённом "контрпримере" для "удалить пользователя", что плохого или невозможного в наличии соответствующей функции в яп и покрытии её тестами?
А нефункционые требования вы будете в виде нефункций выражать? Вы притягивает юзера к вашим тестам. Юзера можно притянуть только к функциональным тестам. Во всех остальных он не фигурирует.
.
P>>·>Опять путаешь цель и средство. Рефакторинг — это средство.
P>>Именно что средство.
·>Дядя Петя... на какой вопрос ты сейчас ответил? Напомню вопрос который я задал: "инлайнинг и т.п. — это средство, а не цель. С какой целью что инлайнить-то?"
Я вам подсветил, что бы полегче прочесть было
P>>·>На какую константу? Значение length() зависит от того, что лежит в данном конкретном экземляре str:
P>>Length это интринсик,
·>Это где и с какого бодуна?
length cводится к
1 получению размера внутреннего массива символов что есть неизменная величина
2 гарантии иммутабельности соответсвующей ссылки. Т.е. никакой
modify(str) не сможет легально изменить длину строки str
3 джит компилятор в курсе 1 и 2 и инлайнит метод length минуя все слои абстракции, включая сам массив и его свойство
Если вы запилите свой класс строк, нарушив или 1, или 2, компилятор ничего вам не сможет заинлайнить.
ваш провайдер времени не обладает свойствами навроде 1 или 2, потому не то что компилятор, а ИДЕ, а часто и разработчик не смогут просто так заинлайнить метод
P>>его сам компилятор заинлайнит, вам не надо заботиться
P>>Инлайнить самому нужно для оптимизации, изменения дизайна, итд
·>Противоречивые параграфы детектед.
Я вижу здесь другой кейс — вы так торопитесь, что обгоняете сами себя. Инлайнить методы нужно для оптимизации или изменения дизайна. В случае c Length инлайн в целях оптимизации выполнит компилятор.
P>>·>А у меня всё круто, т.к. реализация nextFriday() знает, что это отностися к следующей пятнице, то может кешировать одно и то же значение в течение недели и обновлять его по таймеру, например.
P>>Другими, вы здесь предложили наивную реализацию LRU кеша c контролем ттл по таймеру.
·>Угу. С нулевым изменением дизайна.
Ну да — добавить компонент с конфигом и сайд-эффектом это стало вдруг нулевым изменением дизайна
P>>Запрос из кеша это изменение того самого дизайна.
·>Твоего дизайна — да, моего — нет.
Т.е. вы не в курсе, что значение функции закешировать гораздо проще, чем мастырить LRU кеш? Более того — такую кеширующую функцию еще и протестировать проще.
P>>Кеш, как минимум, надо инвалидировать, может и ттл прикрутить, а может и следить, что именно кешировать, а что — нет. Для этого вам нужно будет добавить кеш, настройки к нему, да еще связать правильно и тестами покрыть — что в ваш компонент приходит тот самый кеш а не просто какой то.
·>Кеш — это отдельный компонент.
Вот-вот. Отдельный компонент. В том то и проблема. И вам все это придется мастырить и протаскивать. Только всегда "где то там"
P>>Смотрите выше — вы сами, безо всякой моей подсказки предложили "может кешировать одно и то же значение в течение недели и обновлять его по таймеру, например" что есть тот самый кеш.
·>Именно. А вот ты даже это не смог предложить какого-либо работающего решения, кешировать с ключом по now — это полный бред. И вот тебе придётся для введения кеша в твоём "более гибком коде" перелопатить весь дизайн и тесты, т.к. связывание now и nextFriday у тебя происходит во многих местах и везде надо будет проталкивать этот кеш и его настройки.
Ровно так же как и у вас, только дизайн не надо перелопачивать.
P>>Хотите гибкости — в тесты нужно выносить самый минимум. Как правило это параметры и возвращаемое значение. Вы почему то в этот минимум добавляете все зависимости с их собственным апи.
·>Я не вижу в этом проблему. Ничего не бетонируется. Всё так же рефакторится. Ещё раз напоминаю, что класс с зависимостями это технически та же функция "параметры и возвращаемое значение", но с ЧПФ. Просто некоторые параметры идут через первое "применение": f(p1, p2) <=> new F(p1).apply(p2).
Вы пока что инлайн обеспечить не можете, а замахиваетесь на чтото большее
P>>Вы регулярно приводите примеры лишних строчек, но почему то вам кажется, что у меня кода больше. Код системы это не только функциональная часть, но тестирующая.
·>Я не привожу лишние строчки. Я привожу полный код, а ты весь код просто не показываешь.
И где ваш код nextFriday и всех вариантов инжекции для разных кейсов — клиенское время, серверное, текущие, такое, сякое ?
P>>Я освоил тот метод именно в нулевых, задолго до той самой статьи. С тех пор много чего эволюционировало. Потому я и пишу вам, что вы продолжаете вещать из нулевых, как будто с тех пор ничего и не было.
·>Теперь определись — толи ты сейчас привираешь, что это тебе всё давно знакомо, толи ты просто словоблудил, выразив непонимание что я имею в виду под wiring.
wiring и есть связывание. Код связывания в dependency injection, который вы называете wiring, и код связывания в контроллере — выполняют одно и то же — интеграцию. Разница отражает подходы к дизайну. И то и другое wiring, и то и другое связывание. И задача решается одна и та же. Только свойства решения разные.
P>>P>>By choosing to fulfill dependency contracts with functions rather than classes
·>За деревьями леса не видишь... ты код-то погляди, то же самое частичное применение функций для эмуляции "классов".
Похоже, что пример для Буравчика вы не поняли
P>>В 00х Фаулер сотоварищи топили за тот подход, за который вы топите 20 лет спустя.
·>Потому что тогда он показывал это на языках 00х, а сейчас ровно то же самое показывает на typescript. Та же ж, вид в профиль. Неужели ты до сих пор не въехал в ЧПФ?!
То же, да не то же. Посмотрите где связывание у него, а где связывание у вас.
P>>С тех пор ничего не изменилось.
·>Именно. Кода как не было, так и нет. Так как если появится код, так сразу станет очевидно, что лишних строчек у тебя гораздо больше, да ещё и фреймворки какие-то требуются.
Фремворки и у вас есть, только из 00х. Вы уже признавались в этом.
P>>У вас там написание кода начинается с деплоя на прод?
·>Это где я такое написал? Как вообще такое возможно?!
Вы же рассказываете, что проблему увидите на старте приложения. Прод у вас получает конфиг прода и другие вещи прода. Вот на старте и узнаете, что у вас не так пошло. А надо зафейлить тест, когда вы только-только накидали первую версию контроллера — в этот момент у вас буквально приложения еще нет
P>>А надо во время написания кода которые идут вперемешку с прогном быстрых тестов, и это все за день-неделю-месяц до самого деплоя!
·>Ну чтобы какой-либо тест прогнать — надо стартовать некий код... не? Вот до выполнения первого попавшегося @Test-метода оно и грохнется, где-нибудь в @SetUp.
Вы тестируете не @Setup а иерархию вызовов dependency injection, которая у вас появляется, появляется... на старте приложения. И фейлы ждете с этого момента. А надо гораздо раньше.
Смотрите тот вариант что у Фаулера — его окончательный дизайн позволяет вам фейлить многие вещи когда ничего другого вообще нет — его связывание в одном месте и покрывается тестами
У вас аналог этого связывания будет а хрен его знает когда
·>Не знаю где ты это увидел, имеется в виду мы тестируем прод окружение пытаясь деплоить на прод. Вы, кстати, тестируете уже после деплоя на прод.
Не после — а задолго до. После деплоя прогон тестов это расширенный мониторинг — проверяется выполнимость сценариев пользователя, а не просто healthcheck который вы предлагаете
Почему может поломаться — потому, что приложение работает с кучей внешних зависимостей, которые мы не контролируем.
P>>Ну вот вам и ответ.
·>Напомню вопрос: А как ты будешь такой и-тест писать, если у тебя будет Time.Now? Что ассертить-то будешь? Как отличать два разных вызова Time.Now — клиентский от серверного?
·>Вот пришло тебе в ответе в неком поле значение "2024-01-23". как ты в и-тесте отличишь, что это вычислилось от вызова Time.Now на серверной стороне, а не на клиентской?
Смотрите пример про Колю Петрова, там ответ на этот вопрос
P>>Нам нужны именно фичи, кейсы, сценарии. В строчках кода это не измеряется. Смотрите, как это делают QA.
·>Как ты убедишься, что некая фича покрыта тестами? Или что все фичи и разные corner cases описаны в бизнес-требованиях? Покрытие хоть и ничего не гарантирует, но часто помогает обнаружить пробелы.
Я ж говорю — смотрите, как это у QA делается.
P>>- зарезервировать столик на трех человек на имя Коля Петров на следующую пятницу 21.00
·>Этот пример совершенно никак не отностися к обсуждаемому нами методу nextFriday. Или у тебя в твоём дизайне будет семь методов nextMonday...nextSunday?
Примером по Колю Петрова вам показываю, что такое интеграционные тесты. Вы же в них хотите проверять "а должно вот там вызываться!"
P>>Остальных — от 1000 до 5000 и они занимают 99% времени.
·>Ну вот и говорю хоупейдж. По acc-тестам (селениум, сеть, многопоток, полный фарш) у нас было на порядок больше, выполнялось за 20 минут, правда на небольшом кластере.
Подозреваю, вы их просто подробили, "проверим, что время вон то"
P>>Не нравится метапрограммирование — можете придумать что угодно.
·>И что? Причём тут тесты-то?
Тесты где сравнивается выхлоп из базы не решают проблемы data complexity. Отсюда ясно — что нужно добавлять нечто большее.
P>>Вы там ногой читаете? Запрос не человек, сказать не может. А вот ревьюер может сказать "у нас такого индекса нет, тут будет фуллскан по таблице из десятков миллионов записей с выполнением регекспа и время работы на проде надо ждать десятки минут"
·>Или у вас неимоверно крутые ревьюверы, помнят наизусть сотни таблиц и тысячи индексов, или у вас хоумпейдж. Не все могут себе такое позволить.
Зато сразу ясно, где именно хоум пейдж, и почему ваши тесты работают ажно 20 минут.
P>>Это ж вы сунете тесты туда, где они не работают.
·>Это ты словоблудием занимаешься. Мы обсуждаем именно тесты, а ты тут в сторону доказательного программирования и мета уходишь.
Я вам о том, какие гарантии можно получить вашими тестами, а какие — нельзя.
В вашем случае вы ничего не гарантируете на тех данных, что у вас нет.
P>>Как гарантировать — см пример номер 100500 чуть выше
·>Где чуть выше?
Я вам одно и то же пишу второй год.
P>>У вас данных нет. Или вы щас скажете, что у вас уже есть все данные всех юзеров на будущее тысячелетие?
·>И как тобой предложенное "in: [fn1]" решает эту проблему? Или ты опять в сторону разговор уводишь?
Примерно так же, как
assert.notnull(parameter) в начале метода или
return assert.notEmpty(value) в конце
P>>Наоборот.
·>Ваша цитата: Ага, тесты "как написано" Вам придется написать много больше, чем один тест — как минимум, пространство входов более-менее покрыть. Моками вы вспотеете это покрывать.
Ну ок, значит ничья в этом вопросе
P>>·>Зато есть возможность покрыть известные данные и комбинации тестами. Вы и этого не делаете, а просто имена приватных функций ассертите и буковки сравниваете (тоже, кстати известные). Вот и приходится часами билдить и в проде тесты гонять чтобы хоть что-то как-то обнаружилось.
P>>Зачем вам собеседник, если у вас отличный дуэт с вашей телепатией?
·>В каком месте тут телепатия? Билд занимающий часы — это твои слова. И тесты в проде — тоже.
Вы всё знаете — и сложность проекта, и объемы кода, и начинку тестов, и частоту тех или иных тестов. Просто удивительно, как тонкая телепатия.
P>>Такой тест гарантирует, что всё множество данных будет проходить именно через эту функцию.
·>Не гарантирует. В коде может быть написано "if(thursdayAfterRain())return {...in: [fn42]...}" и тест это не может обнаружить. Ещё раз — тесты ничего не могут гарантировать.
А я и говорю в который раз, что основное совсем не в тестах. В них мы всего лишь фиксируем некоторые свойства решения, что бы досталось потомкам в неискаженном виде.
P>>Я вам привел пример про Колю Петрова. Надеюсь, справитесь
·>Опять врёшь. Это не код.
Почему ж не код? Это в кукумбер переводится практически один к одному.
·>Я это и сказал. Если тут всё равно серверное время, то да, не функциональное, и тесты не пострадают. Тесты сломаются и должны сломаться если ты заменишь источник серверного времени на источник клиентского времени.
А вы точно интеграционные пишите? Чтото мне кажется, вы пишете высокоуровневые юнит-тесты.
·>Я не понимаю какое отношение метапрограммирование имеет к бизнес-требованиям и к тому как писать тесты.
Такое же, как ваши классы, методы, dependency injection итд.
P>>Тесты дают гарантию, что пайплайн устроен нужным мне образом. А свойства результата, следовательно, будут определяться свойствами пайплайна. Тогда вам нужно сосредоточиться на выявлении этих свойств, а не писать одно и то же третий месяц кряду.
·>Тесты не могут давать гарантию.
Тесты именно ради гарантий и пишутся. Только чудес от них ждать не надо.
P>>Смотрите пример про Колю Петрова
·>Не нужен мне пример. Мне нужен код.
Бывает и хуже.
P>>Детали реализации нужно покрывать тестами, что бы первый залётный дятел не разрушил всю цивилизацию:
P>>- оптимизации
·>Это перф-тесты.
Это слишком долго. Фейлы перформанса в большинстве случаев можно обнаруживать прямо во время юнит-тестирования.
P>>- трудноуловимые баги
P>>- секюрити
P>>- всякие другие -ility
P>>- любой сложный код, где data complexity превышает ваше капасити
·>Это "за всё хорошее, против всего плохого". А секьюрити — это бизнес-требование и обязано быть покрыто фукнциональными тестами.
Важно, что бы явные фейлы обнаруживались гораздо раньше старта после деплоя. Тогда будет время подумать, как получить максимум сведений из самого деплоя.
·>"Метапрограммирование тестируется" — зачем?
Тестировать нужно вообще всё, что вам взбредет в голову спроектировать. Тестируюя только крупные куски вы получаете дырявое покрытие
P>>Вы не просто утверждаете это, а идете дальше — утверждаете, что тесты прода это признак наличия багов, не той квалификации и тд.
·>Признак более позднего обнаружения багов.
Они показывают, выполнимы ли сценарии под нагрузкой(при изменениях сети, топологии, итд) или нет.
·>На это и придумали healthchecks (а конкретно liveness probe), проверка, что сервис — operational, явно проверяет себя и свои зависимости, что может отрабатывать трафик без фейлов ещё до того как пошли реальные запросы.
Каким чудом ваш пробы дают ответ, выполнимы или все ключевые сценарии при максимальной нагрузке? Сценарий — это та часть, про Колю Петрова, которая вам непонятна
P>>Какой процесс вас застрахует от фейла на другой стороне которую вы не контролируте?
·>Большая тема. Начни отсюда: https://en.wikipedia.org/wiki/High_availability
Выполнимость сценариев это consistensy всех функций сервиса, а вовсе не high availability, как вы думаете.
P>>Вы наверное с кем то меня путаете. Я ни призывал писать всё руками. С аннотациями похоже снова дело в вашей телепатии. Я ж вам сказал — они для конкретных кейсов. Кодогенерация дает медленный цикл разработки. В некоторых кейсах это можно сократить на недели и месяца. Я привел вам примеры но вы как обычно прошли мимо
·>Кодогенерация сама по себе медленный цикл разаработки не даёт. Любой кодогенератор работает от силы порядка минут.
Именно что сама по себе. Вам надо таскать тот манифест, сихнронизироваться с ним, следить, что бы все имели ту самую версию, тот самый генератор, итд итд
Во многих случаях этого можно избежать
P>>У нас кейс "переписать апи на rust" был 0 раз за 10 лет. Смена технологий от балды — тоже 0 раз. А у вас иначе, каждый месяц всё с нуля на rust переписываете?
·>C++, kotlin, java, groovy, scala (может ещё что забыл)... И таки да, спеки всё-таки есть в виде FIX или хотя бы avro/protobuf. А когда копадаются другие команды/внешине сервисы с подходом "нагенерим из аннотаций" — вечная головная боль интегрироваться с такими.
И ежу понятно — вам максимум с собой легко интегрироваться