Здравствуйте, ·, Вы писали:
P>>Это дополнительные издержки по сравнению с простыми функциями. Вы это сами показали на тестах календаря.
·>Ты просто пытаешься ввести в заблуждение. Да, у меня в тестах добавляется одна строчка кода и собственно всё. Зато в твоём решении добавляется N-строчек в _прод_ коде и требуется больше тестов. Иными словами, ты "убираешь" код, но стесняешься говорить _куда_ этот код убирается.
Чего стесняться — я вам прямо говорю. Переносим код связывания из размытого dependency injection в контроллер. Это дает более простые тесты, более гибкий код, проще мейнтейнить — все что надо у вас перед носом.
P>>И у вас вполне вероятно не будет ни одной функции в джаве "удалить пользователя". А будет например "дропнуть док по хешкоду", "дропнуть записи по номеру телефона", "дропнуть сообщения по емейлу", "отозвать сертификат", "пометить спейс юзера к удалению через месяц", "подготовить выгрузку всех данных юзера", "закинуть зип на aws s3 c ttl 1 месяц"
·>Ну будет же где-то какой-то код, который отвечает за бизнес-действие "удалить пользователя", который в итоге сделает всё вот это перечисленное.
Какой то код — будет. Только с т.з. пользователя эта фича уже работает два релиза. А изменения, как пример, нужны по той причине, что удаление ресурсоемко и можно облегчить основной сервер, передав работу вспомогательному инстанцу. С т.з. пользователя изменений в функциональности нет, ни единой. А вот нефункциональные требования изменились.
Это я вам пример привожу, что функциональность приложения и функции в джаве это две большие разницы.
P>>·>Помогать в чём? Ещё раз — инлайнинг и т.п. — это средство, а не цель. С какой целью что инлайнить-то?
P>>Рефакторинг, очевидно. Его не раз в год нужно делать, а непрерывно — требования меняются постоянно.
·>Опять путаешь цель и средство. Рефакторинг — это средство.
Именно что средство. Если нам доступно больше средств — гибкости больше. Нет инлайнинга — гибкости, как следствие меньше. Вы то до сих пор не показали где же гибкость вашего подхода.
P>>Элементарно — замена вызова на константу. Это работает, т.к. str у вас иммутабельная. А вот текущее время такой особенностью не обладает.
·>На какую константу? Значение length() зависит от того, что лежит в данном конкретном экземляре str:
Length это интринсик, его сам компилятор заинлайнит, вам не надо заботиться
Инлайнить самому нужно для оптимизации, изменения дизайна, итд
·> Кешированию чего? Куда прикручивается кеш — надо смотреть на полное решение, а не на данную конкретнную строчку кода. В твоём решении проблема с кешем будет в другом месте, и её решать будет гораздо сложнее. Точнее ты вообще не сможешь кеш прикрутить, т.к. аргумент now — это текущее время, ВНЕЗАПНО. Ты ключoм в кеше будешь делать текущее время??
·>А у меня всё круто, т.к. реализация nextFriday() знает, что это отностися к следующей пятнице, то может кешировать одно и то же значение в течение недели и обновлять его по таймеру, например.
Другими, вы здесь предложили наивную реализацию LRU кеша c контролем ттл по таймеру.
P>>Дизайн меняется по простой причине — требования меняются непрерывно. Вот появилось нефункциональное требование "перформанс" и мы видим, что CalendarLogic сидит в hotpath и вызывается сто раз с одним и теми же значениями. Опаньки — меняем дизайн.
·> Не меняем дизайн, а оптимизируем внутреннюю реализацию одного метода, ну того конкретного nextFriday(). Менять дизайн на каждый чих — признак полной профессиональной некомпетенции дизайнера решения. Разуй глаза — в сигнатуре "LocalDate nextFriday()" — совершенно не говорится что откуда должно браться — считаться на лету, из кеша, запрашиваться у пользователя или рандомом генериться от погоды на марсе.
Запрос из кеша это изменение того самого дизайна. Кеш, как минимум, надо инвалидировать, может и ттл прикрутить, а может и следить, что именно кешировать, а что — нет. Для этого вам нужно будет добавить кеш, настройки к нему, да еще связать правильно и тестами покрыть — что в ваш компонент приходит тот самый кеш а не просто какой то.
P>>Как это сделать в вашем случае — не ясно. Разве что прокинуть к вашему CalendarLogic еще и MRU, LRU кеш с конфигом "вот такие значения хранить пять минут". Вот уж простор для моков — теперь можно мокать не одно время, а целых три, а можно MRU, LRU обмокать.
·>Толи ты вообще в дизайн не умеешь, толи нарочно какую-то дичь сочиняешь, чтобы мне приписать для strawman fallacy.
Смотрите выше — вы сами, безо всякой моей подсказки предложили "может кешировать одно и то же значение в течение недели и обновлять его по таймеру, например" что есть тот самый кеш.
P>>Что бы просто так, без контекста — это слишком сильное утверждение. Но в целом оно отражает ваш подход к тестированию
·>Моки — это ещё один дополнительный инструмент в копилку. Больше инструментов — больще возможностей решить задачу более эффективно.
Моки в большинстве случаев морозят код, т.е. фиксируют код, бетонируют на времена. Вообще всё что вы пишете в тестах, обладает таким свойством
Хотите гибкости — в тесты нужно выносить самый минимум. Как правило это параметры и возвращаемое значение. Вы почему то в этот минимум добавляете все зависимости с их собственным апи.
P>>Как только вы написали тест, то из него однозначно следует, какой будет АПИ и как будут прокидываться зависимости. Вы поймите — код теста это выражение наших намерений
P>>1 апи
P>>2 зависимости
P>>3 поведение
P>>4 связывание
P>>5 входы-выходы.
P>>По большому счету, идеально свести тест к п5. А вы сюда втаскиваете лошадь размером со слона.
·>апи диктуется требованиями клиентского кода, а не тестами. А зависимости — это уже как мы обеспечиваем работу этого апи.
Если вы в юнит-тестах прокидываете мок в конструктор, то вы делаете выбор дизайна — инжекция зависимостей.
Вариантов, как обеспечить работу апи — множество. Но если вы вписали моки, то после этого варианты закончились — дизайн отлит в граните
P>>Лишняя строчка
·>Прям ужас-ужас. Но это не главное, главное ты наконец-то таки согласился, что твои "таблицы истинности", не пострадали от моков.
Вы регулярно приводите примеры лишних строчек, но почему то вам кажется, что у меня кода больше. Код системы это не только функциональная часть, но тестирующая.
P>>Вот-вот. Статься от 2011го, ссылается на книгу, которую начали писать в 00х на основе опыта накопленного тогда же.
·>И? Поэтому я и не понимаю как эта терминология мимо тебя прошла.
Я освоил тот метод именно в нулевых, задолго до той самой статьи. С тех пор много чего эволюционировало. Потому я и пишу вам, что вы продолжаете вещать из нулевых, как будто с тех пор ничего и не было.
P>>Мы только что выяснили, кто же из нас двоих вещает из нулевых
·>Вот от твоего любимого фаулера, свежачок: https://martinfowler.com/articles/dependency-composition.html
Именно что свежачок. Можете заглянуть сразу в Summary
By choosing to fulfill dependency contracts with functions rather than classes, minimizing the code sharing between modules and driving the design through tests, I can create a system composed of highly discrete, evolvable, but still type-safe modules. If you have similar priorities in your next project, consider adopting some aspects of the approach I have outlined.
В 00х Фаулер сотоварищи топили за тот подход, за который вы топите 20 лет спустя.
P>>Вы уже много раз задавали этот вопрос, и я вам много раз на него отвечал. Отмотайте да посмотрите.
·>Я код прошу показать, но ты упорно скрываешь.
Не валяйте дурака — я вам еще на прошлом заходе давал два варианта
1. из реквеста
2. руками вкидываете
С тех пор ничего не изменилось.
Собственно, вам ни один из них не нравится, потому вы здесь и выступаете. А щас вот пишете, что ничего не видели.
P>>Самое важное сразу в коде метода, а не раскидано абы где по всеей иерархии dependency injection.
·>Субъективщина какая-то.
Это никакая не субъективщина. Все что функционально влияет на контроллер, должно связываться прямо в нем. А вот разная хрень пусть проходит через зависимости, ну вот сериализатор тот же.
Т.е. четкое разделение — функциональные вещи и технологические, а у вас в dependency injection всё подряд — "тут я шью, тут я пью, тут селедку заворачиваю"
P>>Нет, не может быть. Это вам нужно делать явно в самом контроллере, который зафейлится сразу же, на первом тесте который его вызовет.
·>Дык я об этом и талдычу, притом не в абы каком тесте, а конкретно в тесте который тестирует этот конкретную функцию этого конкретного контроллера. А вот в моём случае это придётся делать тоже явно в коде связывания зависимостей и зафейлится если не компилятором, то во время старта, ещё до того как дело дойдёт до запуска каких-либо тестов.
У вас там написание кода начинается с деплоя на прод?
Вы узнаете о проблеме во время самого деплоя! А надо во время написания кода которые идут вперемешку с прогном быстрых тестов, и это все за день-неделю-месяц до самого деплоя!
·>Они не "нужны". Есть другие подходы, которые покрывают эти же кейсы (по крайней мере, подавляющее большинство), но не требуют прогона тестов.
Я и вижу — тестируете деплоем на прод.
P>>Согласно бл. А вы как собираетесь отличать два разных вызова, клиентский от серверного? Вам для этого придется намастырить кучу кода с инжекцией в конструктор, только всё важное будет "где то там"
·>Я уже раза три рассказал. Клиентский и серверный будут иметь разный ожидаемый результат.
Ну вот вам и ответ.
P>>Теоретически — можно. Практически — первый интеграционный тест сваливается, когда вызывает этот контроллер.
·>Если этот тест не забудут написать, конечно. Покрытие-то ты анализировать отказываешься...
Вы совсем недавно делали вид, что понимаете как покрытие работает. А сейчас делаете вид, что уже нет. Покрытие говорит что код вызвался во время теста. Но никак не говорит, какие фичи пройдены.
Нам нужны именно фичи, кейсы, сценарии. В строчках кода это не измеряется. Смотрите, как это делают QA.
·>И как это проверишь в тесте?
·>И я написал почему в твоём коде это неочевидно на ревью.
Это вам неочевидно, т.к. вы топите за другой дизайн. Комбинирование функции в общем случае проще композиции классов. Классы никуда не денутся — одно другому помогает.
P>>Через параметры функции. Дизайн то плоский, а не глубокий, как у вас.
·>Через параметры значения передаются. Откуда они возьмутся-то?
А вы не видите? Весь код перед глазами.
·>У тебя точек привязки — в каждом месте использования nextFriday и никак не тестируется вообще.
Еще раз — точки привязки это часть интеграции, которая тестируется интеграционным тестом. Интеграционные тесты не тестируют кусочки "время прокинуто метод класса куда прокинута бд куда прокинут репозиторий".
Вместо этого интеграция проверяет последовательность, которая вытекает из требований:
— зарезервировать столик на трех человек на имя Коля Петров на следующую пятницу 21.00
— столик на трех человек успешно зарезервирован на имя Коля Петров на 26.01.2024 21.00
— на емейл: столик на трех человек зарезервирован на имя Коля Петров на 26.01.2024 21.00
— пуш нотификация: столик на трех человек зарезервирован на имя Коля Петров на 26.01.2024 21.00
— статус через минуту-час-день: столик на трех человек зарезервирован на имя Коля Петров на 26.01.2024 21.00
— список резервирований: 3 человека, Коля Петров, 26.01.2024 21:00, столик номер 2 рядом со сценой
итд
·>У меня "дешевые" в том смысле, что они выполняются очень быстро. Так вот даже "полу-интеграционные на моках" — выполняются очень быстро, за минуты всё. А у тебя "дешевые" — выполняются полтора часа, но называешь их "дешевыми" так просто, потому что тебе так их нравится называть, без объективных на то причин.
Вы, похоже, без телепатии жить не можете. Вы спросили, сколько времени идут тесты. Я вам ответил, еще и добавил, это все тесты, всего-всего, всех видов. Но вам всё равно телепатия ближе.
Юнит тесты — пару минут от силы. Их до 15тыс ориентировочно. Остальных — от 1000 до 5000 и они занимают 99% времени.
P>>Рефакторинг, оптимизации.
·>Рефакторинг это тоже не задача. А инлайнить для оптимизаций только самые ленивые компиляторы в режиме дебага не умеют.
Большинство компиляторов умеют инлайнить только тривиальные вещи. Расчищать hot path это по прежнему ваша забота
P>>Эта кучка называется data complexity. Я, например, билдером могу гарантировать, что данные будут запрашиваться постранично. Или что всегда будет фильтр по времени, где период не более трех месяцев.
·>Не очень понял, как гарантировать и что именно? И почему то же самое нельзя гарантировать другими способами? И вообще это всё больше похоже на бизнес-требования, чем на проверки каких-то кейсов в тестах.
Это и есть тот, другой способ, основаный на метапрограммировании. Главное, что тесты основаные на проверке выборки из бд этот вопрос принципиально не решают, т.к. мы не знаем, что даст ваш запрос на каких других данных, которых у вас нет.
Не нравится метапрограммирование — можете придумать что угодно.
P>>Соответсвено тестами можно проверить что структура запроса та, что нам надо
·>Ой. Очень интересно увидеть код как ты проверишь тестами по структуре запроса, что у тебя билдер что-то гарантирует.
Я ж вам показал идею. Или вы ждёте тьюринг-полного выхлопа, который умеет еще и мысли читать по вашей методике?
P>>Итого — ваши тесты данных не являются решением, т.к. п 2 у нас обязательный. А вот дополнительные методы, например, инварианты, пред, пост условия, контроль построения запроса, код ревью основанное на понимании работы бд — это дает более-менее осязаемые данные.
·>Я не знаю на какой вопрос ты ответил, но я напомню что я спрашивал: "Как запрос может что-то сказать?". Контекст выделил жирным выше.
Вы там ногой читаете? Запрос не человек, сказать не может. А вот ревьюер может сказать "у нас такого индекса нет, тут будет фуллскан по таблице из десятков миллионов записей с выполнением регекспа и время работы на проде надо ждать десятки минут"
P>>Я вам выше рассказал. Нужна гарантия, что не вернется вдруг чего нибудь не то. Вариантов "не того" у вас по скромной оценке близко к бесконечности.
·>Как ты это предлагаешь _гарантировать_? И причём тут тесты?
Это ж вы сунете тесты туда, где они не работают. Как гарантировать — см пример номер 100500 чуть выше
P>>>>Это вы собираетесь базу тестировать
P>>·>Опять врёшь. И цитаты, ясен пень, не будет.
P>>Вы топите за косвенные проверки запросов, тестируя связку репозиторий+бд. Вот этого оно и есть.
·>Я топлю за то как надо писать тесты, чтобы от них была хоть какая-то польза и поменьше вреда. А не абстрактные "проверки" которые что-то должны "гарантировать" посредством говорящей рыбы запроса.
Ага, за всё хорошее против всего плохого
P>>Пользователи ожидают, что на их данных, которых в ваших тестах нет, будет нужный результат.
·>Если ожидания есть, то какая проблема их добавить в тесты?
У вас данных нет. Или вы щас скажете, что у вас уже есть все данные всех юзеров на будущее тысячелетие?
P>>Вашими тестами эта задача не решается
·>Я показал как решение этой задачи можно тестировать моими тестами. Ты так и не рассказал как она решается вашими тестами.
Я сказал, что тестами эта задача не решается. Вы показали совсем другое — что на ваших данных есть решение. А вот есть ли оно на каких то других — а хрен его знает. Тестами не проверить, т.к. еще не родились юзеры которые создадут эти данные.
Если задача посчитать how many beans make five, то пожалуйста — ваш подход работать будет
А если чтото сложнее — то к тестам не сводится. Именно по этому нужно доказательство корректности алгоритма, запроса, и тд
Собственно, вы раз за разом утверждаете, что с разработкой алгоритмов не знакомы. Мало накидать реализацию и тесты — нужно обосновать и доказать корректность.
P>>Не нужно сводить всё к бл напряму. У вас технологического кода в приложении примерно 99%. Он к бл относится крайне опосредовано — через связывание. А тестировать эти 99% тоже нужно.
·>Именно. Ты меня обвинил в том, что у меня тесты тестируют детали реализации, а на самом деле ты просто с больной головы на здоровую. Но зато у тебя дизайн самый модный!
Наоборот. Это вы пишете что я тестирую те самые детали, которые тестировать, по вашему, не надо.
Похоже, у вас там снова ведущий сменился
·>Зато есть возможность покрыть известные данные и комбинации тестами. Вы и этого не делаете, а просто имена приватных функций ассертите и буковки сравниваете (тоже, кстати известные). Вот и приходится часами билдить и в проде тесты гонять чтобы хоть что-то как-то обнаружилось.
Зачем вам собеседник, если у вас отличный дуэт с вашей телепатией?
P>>Вот оптимизацию такой вещи закрыть тестами, что бы первый же фикс не сломал всю цивилизацию
·>Это всё круто. Каким образом твоё "in: [fn1]" обеспечивает тебя всем множеством данных которое может приходить от юзеров?
Такой тест гарантирует, что всё множество данных будет проходить именно через эту функцию. И если я знаю, что у неё нужные мне свойства(тестами не решается), то результат будет обладать нужными мне свойствами. Например, если функция никогда не возвращает null, то вы можете и не бояться "а вдруг там всё таки null" и сократить hot path
P>>В интеграционном тесте можно сделать то же самое.
·>Как? Код в студию. Вот в куче кода кучи контроллеров где-то у тебя написано Time.Now. Как ты это проверишь в интеграционном тесте что некий результат содержит именно что-то вычисленное от Now, а не что-то другое?
Я вам привел пример про Колю Петрова. Надеюсь, справитесь
·>Не это, а то что вначале мы использовали серверное время, а потом вдруг стали использовать клиентское — это изменение функциональное, т.к. источники времени — принципиально разные и влияют на наблюдаемый результат. Если бы было время клиентское изначально, то никаке функциональные тесты не пострадают.
Нет, это не функциональное изменение. Мы просто перенесли процессинг в очередь. С точки зрения пользователя ничего не изменилось.
Похоже, вы не сильно в курсе разницы между функциональными требованиями и нефукнциональными.
Вот еще один пример:
Функциональное — пользователь должен будет отслеживать прогресс в своем профайле
Нефункциональное — пользователь увидит результат позже, чем раньше
P>>Метапрограммирование дает возможность зафиксировать свойства запроса, которые вы с вашими тестами сделать не сможете, никоим образом.
·>А ещё я кофе моими тестами сварить не смогу. Что сказать-то хотел?
·>Напомню, мы сравниваем тестирование ожиданий бизнес-требований твоими тестами с "in: [fn1]" vs моими тестами c "save/find". Так причём тут метапрограммирование?
Я вам объяснил уже много раз. Вы точно понимаете, что такое метапрограммирование?
P>>Может. Это дополнительные гарантии, сверх тех, что вы можете получить прогоном на тестовых данных. Потому издержки обоснованы.
·>Гы. Опять у тебя тесты гарантии дают.
Тесты дают гарантию, что пайплайн устроен нужным мне образом. А свойства результата, следовательно, будут определяться свойствами пайплайна. Тогда вам нужно сосредоточиться на выявлении этих свойств, а не писать одно и то же третий месяц кряду.
P>>Вот вот — прибиваете контроллер к тестам на моках.
·>Ты так говоришь, как будто это что-то плохое.
Щас у вас ведущий сменится, и вы начнете снова отрицать.
P>>Тогда можно сравнить выхлоп, тот или не тот.
·>Так я тебя и спрашиваю как ты будешь сравнивать выхлоп зависящий от Time.Now — тот он или не тот?
Смотрите пример про Колю Петрова
P>>Этим протаскиванием вы бетонируете весь дизайн. Смена дизайна = переписывание тестов.
·>Нет.
Вы прибили контролер к тестам на моках. А тут надо процессинг вытеснить в очередь. Приплыли.
P>>Покрыть перформанс тестами все вырожденные кейсы вам время жизни вселенной не позволит. Некоторые детали реализации нужно фиксировать обычыными тестами.
·>Обычные тесты не могут тестировать перф. А детали реализации лучше не тестировать.
Детали реализации нужно покрывать тестами, что бы первый залётный дятел не разрушил всю цивилизацию:
— оптимизации
— трудноуловимые баги
— секюрити
— всякие другие -ility
— любой сложный код, где data complexity превышает ваше капасити
P>>Метапрограммирование тестируется там, где оно применяется в силу естественных причин. Если у вас запросы тривиальные, вам такое не нужно.
·>Ты не понял. Надо тестировать не метапрограммирование, а ожидания.
Вы снова решили в слова поиграть.
P>>Неэффективно. Для многих приложений час невалидной работы может стоит любых денег. Дешевле запустить тесты на проде — час-два и у вас информация по всем функциям.
·>Ну никто нам не позволит делать "тестовые" сделки на прод системах. Каждая сделка — это реальные деньги и реальная отчётность перед регуляторами. Так что "информацию по всем функциям" нам приходится выдавать ещё до аппрува релиза.
Вы не просто утверждаете это, а идете дальше — утверждаете, что тесты прода это признак наличия багов, не той квалификации и тд.
Возможно для вашей биржы или что это там еще, тесты на проде слишком рискованы. Это ж не значит, что и везде так же.
P>>Вы снова включили телепатию. Сертификат может обновиться не у вашего приложения, а у какого нибудь из внешних сервисов. Мониторинг вам скажет, когда N юзеров напорются на это и у вас начнет расти соответсвующая метрика.
·>Нет, мониторинг нам скажет, что сессия с таким-то сервисом сломалась и свистайте всех на верх.
А у вас один юзер в день что ли? Пошел трафик, а потом вжик — идут фейлы.
·>Такое — major incident и соответствующий разбор полётов с пересмотром всех процессов — как такое допустили и определение действий, чтобы такое больше не произошло.
Какой процесс вас застрахует от фейла на другой стороне которую вы не контролируте?
P>>У вас есть иллюзия, что ваши тесты гарантируют отсутствие лишнего выхлопа на данных, которые у вас отсутсвуют.
·>У меня такой иллюзии нет, ты опять насочинял, я такое нигде не писал. О гарантиях в тестах фантазировать любишь только ты.
Вы же топите за тесты и утверждаете, что они чего то там решают. Ниже пример этого
P>>·>Да так же, как у тебя. Только ассертить не буквальный текст запроса, а результат его выполнения
P>>
·>Угу, вот так вот просто.
Вот этот самый пример — вы здесь утверждаете, что тестами решите проблему с data complexity. Или пишете тесты непонятно для чего, или же не в курсе data complexity
·>Ну я хотя бы могу начать допиливать этот самый swagger, чтобы генератор таки стал понимать.
·>А у тебя всё гораздо хуже. Ты пишешь аннотации, конфиги, тенантов, реализацию, веб-сервер, потом у тебя наконец-то http-запрос выдаётся этот же самый swagger по этим аннотациям, который ВНЕЗАПНО ни один генератор кода не понимает. И это реальная ситуация, которую помнится уже обсуждали пару лет назад и твоим решением было "ну пусть клиенты пишут всё ручками", т.к. перепилить свои аннотации и http-запрос ты уже не сможешь, ибо проще пристрелить.
Вы наверное с кем то меня путаете. Я ни призывал писать всё руками. С аннотациями похоже снова дело в вашей телепатии. Я ж вам сказал — они для конкретных кейсов. Кодогенерация дает медленный цикл разработки. В некоторых кейсах это можно сократить на недели и месяца. Я привел вам примеры но вы как обычно прошли мимо
P>>Вы похоже не читаете. Какой смысл вам что либо рассказывать?
P>>1. когда стартуем проект, где центральная часть это АПИ. Идем через design-first, формат файла и расширение — любое, см. выше
P>>2. готовый проект — уже поздно топить за design-first, просто генерим артефакты
P>>3. переписываем проект на rust — снова, нам design-first не нужен, это вариация предыдущего кейса
·>Не понял, как из 1 получилось 2?
Никак — это разные кейсы.
P>>У вас точно меняется контингент, или в голове, или за компом:
P>>P>>Если вы захотите переписать свой сервис на другой ЯП, ну там с js на rust — что вы будете делать со своими аннотациями?
P>>Переписывание предполагает, что у нас уже есть и АПИ, и реализация этого АПИ. Отсюда ясно, что проще сгенерировать артефакт, чем изначально держаться за конкретный формат и топить за design-first
·>Ага, верно, проще прыгать чем думать. Не спорю.
У нас кейс "переписать апи на rust" был 0 раз за 10 лет. Смена технологий от балды — тоже 0 раз. А у вас иначе, каждый месяц всё с нуля на rust переписываете?