Информация об изменениях

Сообщение Re[42]: Что такое Dependency Rejection от 11.01.2024 22:32

Изменено 11.01.2024 23:23 ·

Re[42]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>>>Вы уже показывали, когда мокали репозиторий ради тестов контролера, а потом скромно замолчали на вопрос, что делать, если меняется дизайн

P>·>Вопрос я не понял, мол что делать если что-то меняется, ответил как мог — если что-то меняется, то эээ... надо что-то менять. Если тебя интересует конкретная проблема, сформулируй с примерами кода.
P>Вы мок затачивали на все тесты, или по моку на каждый?
P>1. На все — все ваши тесты неявно теперь зависят от мока. И если вы закостылили мок, то заточили тесты под синтетику, код — под такие тесты.
P>2. Если по моку на каждый — можно хоть ревью сделать. Кода больше, но и контроля больше.
Я не понимаю что такое заточка мока. Код с моками это просто код. Там где код общий, там он переиспользуется из разных мест. Там где не общий, там не переиспользуется. Мок это просто механизм передачи данных. Ровно такой же вопрос я могу спросить про твои параметры.

P>>>Я вам про функциональные требования, а вы мне про функции в коде. Проверка склейки — это вайтбокс тест уровня юнит-тестов. В интеграционных тестах мы проверяем ожидания вызывающей стороны. А её точно ничего про склейку не известно.

P>·>"следующая пятница текущего времени" — вполне функциональное требование. В интеграционных тестах ты прямо заявил, что никак не сможешь проверить это ожидание.
P>Проверка склейки — такого не будет. А вот поддерживает ли приложение такую функцию — нужно проверять интеграционным тестым.
Не нужно. Интеграционный тест проверяет интеграцию, что компоненты работают вместе. А не каждую функцию приложения.

P>И ваши моки не избавляют этого.

Моки не избавляют. А помогают избавляться. Помогает дизайн. Вместо того, чтобы всё распылять на мелкие кусочки, которые теперь надо скурпулёзно инетгрировать, можно делать относительно крупные компоненты, которые проще интегрировать вместе.

P>>>Есть такая штука. От безысходности — если вы такой дизайн построили. Искусственное понижение уровня тестирования.

P>·>Ну да, верно. Ты так говоришь, как будто что-то плохое. Чем больше мы можем проверять низкоуровневыми тестами, тем лучше. "Искусственно" — тоже верно, мы режем зависимости там, где они самые тяжелые, требуют больше ресурсов, замедляют билд. "Безысходность" — потому что альтернативы — хуже.
P>Самосбывающееся пророчество — обычно безысходность очень быстро наступает в том случае, когда изначально заложились на моки b монолитный дизайн.
P>Вариантов дизайна всегда больше одного
Именно. Это всё субъективщина. Эти варианты у тебя меняются каждые год два, как женские шляпки. А вот требование ресурсов, использование сети-тредов-диска — это объективные критерии. На них я и ориентируюсь, а не на последние веяния моды.

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

P>·>Что за "физически"? Тезис Чёрча знаешь? Любой код можно переписать на чистый, хаскель неплохо это доказывает. Вопрос — накой?!
P>Есть же пример Буравчика. Вам мало, надо к абсурду свести?
В смысле ты хочешь сказать, что его пример на хаскель не переписывается? Переписыватеся, с монадами и т.п. Но проще он не станет. Поэтому вопрос и остаётся — накой. Твои предложения "улучшить" я не понял в чём же улучшение.

P>>> Тогда приходят на помощь моки — как вы сами и сказали, замокали один компонент, что бы проверить остальные два.

P>·>Замокали "плохой", тяжёлый компонент, требующий файловой системы, сети, многопоточки, чтобы хорошо и легко проверить остальные два на саму логику и бизнес-требования.
P>В стиле ТДД — написали тесты на моках, а потом и дизайн вышел соответствующим
Нет.

P>>>Или как у Буравчика — мутабельные вычисления, кучка зависимостей да два ифа.

P>·>Ну "правильного" дизайна ты предолжить просто не смог. Т.к. это более менее реальный проект с нетривиальной бизнес-логикой. А твои штуки вменяемо работают только на хоумпейджах.
P>Какой еще "правильный"? Я продемонстрировал, что минимальными усилиями можно свести тесты к таблице истинности и хранить все это максимально близко к коду.
Я видимо не понял. Где какие таблицы?

P>·>Связывание находится в wiring code. Контроллер это просто ещё один компонент, ничем принципиальным от других не отличается.

P>Связывание и есть wiring код. Основная обязанность контроллера — именно это. И тестировать связку, как следствие.
Нет, обязанность контрллера связывать входящие запросы с бизнес-логикой. wiring code — это отдельный код для связывания компонент друг с другом.

P>Если же вы сторонник жирных контроллеров, конечно же вам придется мокать всё на свете. Не только time, а еще и sin и cos

Это твои фантазии, спорить не буду.

P>>>Ы-ы-ы — всё это придется затыкать юнит-тестами, компилер вам не помощник.

P>·>Покажи код где там какой типизации не хватает в java. И не просто общие слова, а не забывай про контекст обсуждения.
P>Я ж вам перечислил. Вы хотите еще и фичи статической типизации докинуть к нынешней теме?
Мне пофиг на общие слова и перечисления. Я прошу код. Что с чем ты собираешься согласовывать, зачем, и причём тут система типов.

P>>>blackbox — тестируем исходя из функциональных требований.

P>·>И? "текущее время" vs "данный момент времени" — это функциональные требования. Причём тут вызовы функций?
P>Вы тестируете, что результат зависит от конкретного вызова. Сами же про это и пишете.
В следующий раз когда ты мне будешь приписывать очередную фантазию, цитируй.

P>>>Вы сейчас в слова играете. Если не протаскивать зависимости, в CalendarLogic будет null во время вызова, буквально. Это значит, вы всю мелочевку повесите на dependency injection.

P>·>Что за ерунда? Откуда там null возьмётся? Что за "повесите на dependency injection"? источник времени — это зависимость CalendarLogic, инжектится через конструктор. ЧПФ, забыл опять?
P>Сами спросили — сами ответили.
Откуда _у тебя_ null берётся? У меня никаких null тут нет.

P>>>А вот вам кейс — трохи поменяли архитектуру, и БЛ начала вызываться через очередь, с задержкой от часов до дней. А время должно отсчитываться с момента когда юзер нажал кнопку.

P>·>Поменяется InstantSource, будет добывать время не из system clock, а из таймстампа в очереди. Это вообще никакое не архитектурное изменение, а одна строчка в wiring code — что инжектить как источник времени.
P>О — InstantSource появился. Значит инжектиться может не один единственный вариант с Now, а минимум два.
А ты вообще код мой читал? В первой строчке код-сниппета
Автор: ·
Дата: 28.12.23
. Ты по-моему сам со своими фантазиями споришь, я тебе только мешаю.

P>Где у вас тестирование инжекции? А ну как на проде все инжектиться будет по старому?

В прод-classpath не видны моки и вообще вся тестовая белиберда. Код не скомпилится просто, если туда правильную зависимость не вписать.

P>>>В моем случае всех изменений — вместо прямого вызова делаем отложеный.

P>·>Что за отложенный вызов? Вызов где?
P>А как вы понимаете, что такое отложеный вызов? Недавно вам непонятно было что такое dependency injection, потом выяснилось, что все вам известно.
Мне не было понятно, что ты под этим понимаешь.

P>Тут похоже вам снова надо с самим собой поговорить

Ну ссылку на вики дай.

P>>>Интересно, что может быть и так и так — часть вещей считаем сейчас, часть — потом, а время используется и там и там, как текущее, так и предзаписанное.

P>·>Ну будет два InstantSource в разных местах.
P>Вот-вот. Как вы отличите что первый и второй не перепутаны? Это ж ваша любимая тема — надо проверить, что передали то самое!
Устанавливаем в тестах, что "у нас на сервере now это 21:00:01, а на клиенте "21:00:02". И в тестах можно ассертить что в нужных случаях ожидаемое время. Что ты будешь делать со своим синлтоном Time.Now — ты так и не рассказал. Вообще я не понимаю как ты такое можешь использовать и тем более тестировать, просвети.
Что синглтоны — плохо учат ещё в детском садике, ясельной группе.

P>>>Это вам пример, как изменение дизайна вносит хаос именно в моковую часть тестов.

P>·>В моках надо будет просто сделать два источника времени, у и ассертить, что в нужных местах используется время из ожидаемого источника.
P>А еще можно обойтись без моков И свести к таблице истинности, поскольку интеграционный тест всё равно понадобится.
Ты заявил, что никак не сможешь время протестировать.

P>>>Ога — каким чудом компилер узнает, что в отложеном процессинге время в той самой функции надо брать с момента инициирования запроса, а в аудите и то, и другое?

P>·>Это будет две функции. ЧПФ, слышал?
P>Я в курсе: "ассертим что вызываем с тем параметром а не этим"
Бред.

P>·>У тебя много, на каждый сценарий.

P>Не всё так страшно — мы упрощаем не только бл, но и связывание. Посколько у вас больше одного провайдера для зависимости, всё может играть по разному в зависимости от сценария.
P>В одном сценарии у вас один провайдер времени, в другом — другой. Опаньки! Только вы это размазали по коду.
Опять фантазируешь что-то.

P>>>2 код в них тривиальный, линейный

P>·>Да, но кода этого много накопипасчено.
P>Ну и что?
Что каждую копию надо покрывать тестом. Ты правда сейчас собираешь копи-пасту продвигать как правильный дизайн?

P>>>Вы только что говорили о чудесной поддержке иде именно вашего дизайна. Покажите это чудо. Мне вот ИДЕ спокойно может заинлайнить любое из мест вызова nextFriday(now)

P>·>Круто, но зачем? Задачу-то какую решаем?
P>Затем, что признак хорошего кода это его гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить, итд, да хоть вовсе от него избавиться.
Если при этом нужно плодить копипасту, то это явно что-то не то. И неясно причём тут инлайн и гибкость кода. str.length() — это негибкий код?!

P>·>Не с любыми, а только с теми, которые тестируют детали имплементации, а не логику. Поэтому я и говорю, что _эти_ ю-тесты — не нужны. Ибо всё ровно то же придётся покрывать и-тестами с субд. Ты можешь объяснить зачем ты пишешь такие ю-тесты? Повторюсь, эти тесты не только бесполезны, но и вредны, т.к. хрупкие и усложняют внесение изменений.

P>Вы так и не рассказали, как будете решать ту самую задачу с фильтрами.
Рассказал и даже код показал,
Автор: ·
Дата: 28.12.23
насколько понял твою задачу. Если ещё остались вопросы, спрашивай.

P>>>Для того нам и нужна таблица истинности рядом с кодом и его тестами — такое можно только другими методами проверять

P>·>Дело в том, что в случае субд у тебя кода субд нет.
P>И не надо.
Надо, если хотим тестировать поведение, а не детали имплементации.

P>>>Это выхлоп билдера. С чего вы взяли, что такой код надо руками выписывать?

P>·>Это какой-то небуквальный выхлоп или что? Выхлоп какого билдера?
P>Билдера запросов к базе данных. Вы что думаете, все sql запросы руками пишутся?
Да похрен как они пишутся, хоть левой пяткой. Это всё детали реализации. Главное что они работают ожидаемым способом.

P> Кое что, для аналитики — возможно. Но это большей частью всё равно перегоняется в орм или билдер. Есть орм — можно проверять построение логики запросов орм.

В смысле ты в своих тестах linq/etc тестируешь? Зачем? У linq свои тесты есть.

P>>>Соответсвенно, и тестировать нужно соответствующим методом

P>·>Ты вроде сам заявлял, что тестировать нужно ожидания. Какое отношение метапрограммирование имеет к бизнес-требованиям или ожиданиям?
P>Запрос к базе должен соответсвовать бизнес-требованиям. Только проверяем мы это в т.ч. посредством метапрограммирования.
Что за бред? У тебя в FRD прописаны тексты запросов??! Прям где-то у вас есть бизнес-требование "select * from users where id=?" и "in: [fn1]"?! Приходит к вам клиент и говорит "а мне очень нужно чтобы out: [fn2], когда можете сделать?"

P>>>Ваша альтернатива — забить на data complexity, так себе идея.

P>·>Цитату в студию или признавайся, что опять соврал.
Цитаты нет. Т.е. соврал.

P>Вы до сих пор не привели решение кроме "ищем в бд"

Я код
Автор: ·
Дата: 28.12.23
привёл. Что не устраивает-то??! "ищем" — это "find", английский язык знаешь?

P>>>do_logic и есть или юз-кейс, или контроллер.

P>·>Я имею в виду вот этот твой do_logic. Это не юзкейс. Это код-лапша.
P>Сравнивать нужно не как выглядит, а всё цельные решения, включая моки всех сортов, dependency injection, размазаную и скрытую, тесты которые без таблицы истинности итд.
Я уже всё рассказал и показал. _Всё_. Нет ничего скрытого.

P>·>Ты читать умеешь? Я пишу "некий аспект", ты возражаешь "Все аспекты". Я где-то требовал тестировать все аспекты, кроме как в твоих фантазиях? Я спросил как тестировать один конкретный тривиальный аспект, который элементарно тестируется с моим дизайном, а ты не смог, у тебя солнце погасло.

P>У вас плохо с логикой — если нет возможности тестировать все аспекты, а это невозможно, значит в каждом конкретном случае какие то аспекты будут непротестированы.
P>Ваш тривиальный аспект это "вызываем с нужным параметром" ?
Нет. Код смотри.

P>>>·>Я топлю за вайтбокс в том смысле, что тест-кейсы пишутся со знанием что происходит внутри, чтобы понимать какие могут быть corner-cases. Но сами входные данные и ассерты теста должны соответствовать бизнес-требованиям (для данного id возвращается правильно сконструированный User). Следовательно, изменение имплементации всё ещё должно сохранять тесты зелёными.

P>>>Нет, не должно.
P>·>Должно, т.к. это тестирование бизнес-требования.
P>Должно, только ваш nextFriday поломал вообще всё, т.к. ему надо прописать другие значения, а без этого или все тесты красные, или зелены при гарантировано нерабочей системе.
Фантазии.

P>>>Смотрите про тривиальный перенос вызова из одного места в другое — ваш nextFriday поломал вообще всё

P>·>Куда смотреть? Поломал что?
P>Вам надо мок подфиксить, что бы время было той системы
Чё?

P>>>·>Вот твой код "{sql: 'select * from users where id=?', params: [id], transformations: {in: [fn1],out: [fn2]}" — таким свойством не обладает, т.к. проверяет детали реализации (текст запроса, порядок ?-парамов, имена приватных функций), а не поведение. Этот тест будет ломаться от каждого чиха.

P>>>Кое что сломается. Поскольку это метапрограммирование, то это ожидаемый результат.
P>·>Ожидаемый кем/зачем?
P>Наша задача гарантировать что запросы соответствуют бизнес-требованиям. Что бы это гарантировать, я выбрал реализацию с метапрограммированием. Вы, очевидно, выбрали другое — гонять горстку данных к базе и обратно,
Покажи пример бизнес-требования.

P>и у вас кейс с вырожденным фильром до сих пор не покрыт тестом.

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

P>>>Что бы от каждого чиха — такого нету. Запросы меняются с изменением бизнес логики.

P>·>Я тебе приводил пример — заменить звёздочку на список полей — это чих, а не изменение бизнес логики.
P>Не знаю, что у вас с этим за проблема, тут рукописные логи годами ходят в одном и том же виде, тригерят эвенты, и ничего. А гарантировать сохранность стиля генерируемого кода почему то вам кажется рокетсаенсом.
Да причём тут стиль? Это мелкий чих. Я могу переписать например "SELECT ... X OR Y" на "UNION SELECT ... X ... SELECT ... Y" для оптитмизации. У тебя все тесты посыпятся и любая ошибка — увидишь после деплоя и прогона многочасовых тестов, в лучшем случае (а скорее всего только в проде). А у меня только те тесты грохнутся, если я реально ошибусь и что-то поломаю в поведении, ещё до коммита.

P>>>В интеграционных тестах мы не проверяем точки — мы проверяем саму сборку.

P>·>Если сборка идёт в трёх точках — проверить легко. Если сборка идёт в трёхсот точках — проверить сложно.
P>Вы почемуто забываете про dependency injection — это такой код, который труднее всего покрыть тестами, а между тем у него чудовищное влияние на систему.
P>Потому и стоит менять дизайн что бы это контролировать, а через di протаскивать только технологические зависимости.
Зато этот код очень простой, линейный и на 99% покрывается компилятором. Остальной 1% покрывается стартом приложения.

P>>>То, что вы пишете — именно вайтбокс тест, тавтологичный "проверим что подставилось время клиента" — это и есть суть проблемы.

P>·>Почему вайтбокс? Тест бизнес-требования — "в ответе приходит правильное время".
P>Если это про некоторую сборку, когда вы проверяете результат, то все годится. Только вам здесь надо вбросить "время клиента" каким то раком. А потом проверить, что вбросили удачно.
Ну да, но только не раком, а моком.

P>>>Вызовем как то иначе, но обеспечим результат тот же, ваш тест сломается т.к. он по прежнему будет долбить "проверим что подставилось время клиента"

P>·>Как ты _проверишь_, что результат тот же и соответствует ожиданиям?
P>Очень просто — у нас уже есть тесты, одни должны сохраниться без каких либо изменений.
Я спросил как сделать так, чтобы такие тесты были, ты ответил
Автор: Pauel
Дата: 31.12.23
"Никак.". Т.е. тестов у вас таких просто нет.

P>Раз мы их спроектировали, а не наструячили от балды, убедились в этом, то они рассматриваются, как корректные.

P>Теперь изменение реализации должно сохранить тесты зелеными
Эээ.. ну да... верно. После любого такого изменения ни один тест не покраснеет. no matter what.

P>·>

P>·>·>Как ты тестируешь что построенный запрос хотя бы синтаксически корректен?
P>·>тот самый интеграционный тест, на некорретном запросе он упадет
P>·>

P>·>Т.е. у тебя есть туева хуча ю-тестов для каждого сценария, которые проверяют _тексты_ запросов, но для проверки, что текст запроса хотя бы корректен — приходится ещё и и-тестом каждый запрос проверить для ровно тех же сценариев.
P>интеграционные тесты строятся по функциям, а не по запросам к базе.
Я о чём и говорю. В каких-то случаях у тебя могут меняться запросы. Ты уже сам говорил о случае с пустым значением, которое ты заявил, что и-тестами тестировать не собираешься.
И это могут быть нефункиональные требования, а какие-то технические детали, оптимизации. Простой пример. Скажем, если идёт выборка по небольшому списку параметров, то строится запрос с "IN (?,?)", иначе создаётся временная табличка и с ней джойнится.

P>А вот запросы к базе вещь слишком сложная и ответственная, потому стоит использовать разные инструменты, а не рассказывать, что вам запретили метапрограммирование

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

P>>>Для юнит-тестов это как раз нормально, их предназначение — тестировать всю мелочевку, из за которой у вас вообще что угодно можно происходить

P>·>Дело не в мелочёвке-большичёвке, а в том, что ты тестируешь код как написано, а не ожидания/бизнес-требования. Ровно то, чем ты меня пытался попрекать.
P>Похоже, что вам действительно ктото запретил метапрограммирование
Фантазируешь.

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

P>·>Мрак.
P>Видите — не так, как вы привыкли и сразу ужос-ужос-мрак-и-смэрть
Ну вроде это уже вс проходили и забили. Заталкивать тесты в прод-код... зачем??!

P>>>И как же вы решаете проблемы с data complexity которая тестами не решается используя исключительно косвенные тесты?

P>·>А конкретнее? С примером кода.
Именно. Примера кода я так и не дождусь.

P>Это я вам вопрос задал, вообще говоря. Те самые фильтры — вполне себе хороший пример.

Для тебя — хороший. Потому что у тебя оно в голове, т.к. ты с этим трахаешься последние пол года, не меньше, похоже. Я вообще плохо понимаю о чём идёт речь и с какими конкретными проблемами ты столкнулся. Я ведь не телепат... А подробностей от тебя не дождёшься. Приведи код, сформулируй проблему, я может и погляжу подробнее.

P>>>Это выхлоп билдера — в нем связывание, конверсия, маппинг и тд. Т.е. у нас кейс с метапрограммированием — результат фукнции это код другой функции. И я использую ровно тот же подход что и везде — сравнение результата. Только в данном случае наш результат это код.

P>·>Зачем нам тестировать выхлоп билдера? У билдера должны быть свои тесты.
P>Это и есть тесты билдера. Только конкретного, в котором заложено много различных вырожденных кейсов. linq не в курсе, что вы вытаскиваете всю базу, ему по барабану. Мне — нет.
Linq выдаёт список результатов — вот и проверяй, что в этом списке — вся база или не вся.

P>>>Например — фильтр вырождается в true. А вот если мы накладываем ограничения на результирующий запрос, у нас эта часть исключается принципиально.

P>·>Не понимаю проблему. Если фильтр выродится в true, то субд вернёт те записи, которые мы не ожидаем, тест провалится.
P>Ну так найдите эту комбинацию. А их, мягко говоря, немало. Как будете искать?
Анализом требовний, кода, данных, етс. Обычный corner case analysis. А как вы будете искать?

P>>>Простой вопрос был — как ваше доказательство проверит само себя если другой коллега начнет писать код как ему вздумается?

P>·>Верификатор выдаст ошибку. Почитай про какой-нибудь coq что-ли.
P>И вы так прямо и втащите coq в любом проект на джаве?
Нет. А вы таки втащили доказательное программированипе в js?

P>·>Ну будет в коммите одном из десятка тестов вырожденный фильтр... И? Особенно весёлые штуки типа "A = B OR A <> B" из-за немножко не так поставленных скобочкек. Ты как равьювер будешь смотреть тесты и в уме интерпретировать sql запросы?

P>Еще раз — фильтр не хардкодится, а строится.
Я потерял суть. О чём речь вообще? Пример в студи. КОД!

P>>>Наоборот. Руками затейники выдают такое, что ни один генератор проглотить не может. А если вы начинаете с аннотаций или хотя бы интерфейсов, то уж точно ясно, что это реализуемо.

P>·>Верно, но называется это code-first со всеми последствиями.
P>Нет, не называется. Разница между code-first и design-first не формате и расширении файла, как вам кажется, а в очередности деливери.
Верно. В design-first деливерится манифест first. А не http-приложение, генерящее манифест.

P>В том то и дело — ваша задача накидать интерфейсы-модели так, что бы имплементации было 0. Тогда можно деливерить сразу, а вот будет ли деливериться имплементация, и когда — уже дело десятое.

Ну да. Откуда же берутся конфиги/тенанты/http-запросы?

P>Посмотрите grapql, grpc — API и есть код, вопрос в том, что вы деливерите. Полурабочий каркас приложения — это code-first. А если описание, которое могут взять другие команды, и работать независимо — design first

Расскажи где в graphql/grpc можно найти конфиги/аннотации/переменные окружения?

P>>>Вы вместо вопроса слишком злоупотребляете телепатией. АПИ, если отдаётся, то в большинстве случаев уже ничего генерить не надо. Например, согласовали интерфейс, зафиксировали, и каждый пошел работать над своей реализацией.

P>·>Ты опять врёшь. Не телепатией, а из твоих слов: "Манифест генерируется по запросу, обычно http", противоречие выделил жирным. Или ты похоже просто в прыжке переобуваешься.
P>Вы вместо уточнения пытаетесь в телепатию играть. Если у нас готовое приложение, то конечно же генерируется — и странно было бы иначе делать.
Угу. ЧТД. "готовое приложение" == "code-first".

P>А если у нас ничего нет, только АПИ, то пишите АПИ сразу так, что бы быстрее его заделиверить и цикл разработки был максимально короткий.

Откуда в апи у тебя возьмутся тенаты? Или ты просто словоблудием занимаешься? Когда удобно имеешь в виду gprc, а когда удобно вкидываешь про готовое приложение — лишь бы собеседника переспорить.

P>>>Вы похоже не вдупляте — design-first = вначале деливерим АПИ, а потом уже реализация, куда входят переменные, конфиги, логика тенантов итд.

P>·>http-сервер это такой АПИ... Угу-угу.
P>Я вам про интерфейсы, а вы сюда http-server тащите. Дурака валяете.
Не ври. http втащил ты. Цитиую: "по запросу, обычно http".
Re[42]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>>>Вы уже показывали, когда мокали репозиторий ради тестов контролера, а потом скромно замолчали на вопрос, что делать, если меняется дизайн

P>·>Вопрос я не понял, мол что делать если что-то меняется, ответил как мог — если что-то меняется, то эээ... надо что-то менять. Если тебя интересует конкретная проблема, сформулируй с примерами кода.
P>Вы мок затачивали на все тесты, или по моку на каждый?
P>1. На все — все ваши тесты неявно теперь зависят от мока. И если вы закостылили мок, то заточили тесты под синтетику, код — под такие тесты.
P>2. Если по моку на каждый — можно хоть ревью сделать. Кода больше, но и контроля больше.
Я не понимаю что такое заточка мока. Код с моками это просто код. Там где код общий, там он переиспользуется из разных мест. Там где не общий, там не переиспользуется. Мок это просто механизм передачи данных. Ровно такой же вопрос я могу спросить про твои параметры.

P>>>Я вам про функциональные требования, а вы мне про функции в коде. Проверка склейки — это вайтбокс тест уровня юнит-тестов. В интеграционных тестах мы проверяем ожидания вызывающей стороны. А её точно ничего про склейку не известно.

P>·>"следующая пятница текущего времени" — вполне функциональное требование. В интеграционных тестах ты прямо заявил, что никак не сможешь проверить это ожидание.
P>Проверка склейки — такого не будет. А вот поддерживает ли приложение такую функцию — нужно проверять интеграционным тестым.
Не всегда нужно, в этом и суть. Интеграционный тест проверяет интеграцию, что компоненты работают вместе. А не каждую функцию приложения.

P>И ваши моки не избавляют этого.

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

P>>>Есть такая штука. От безысходности — если вы такой дизайн построили. Искусственное понижение уровня тестирования.

P>·>Ну да, верно. Ты так говоришь, как будто что-то плохое. Чем больше мы можем проверять низкоуровневыми тестами, тем лучше. "Искусственно" — тоже верно, мы режем зависимости там, где они самые тяжелые, требуют больше ресурсов, замедляют билд. "Безысходность" — потому что альтернативы — хуже.
P>Самосбывающееся пророчество — обычно безысходность очень быстро наступает в том случае, когда изначально заложились на моки b монолитный дизайн.
P>Вариантов дизайна всегда больше одного
Именно. Это всё субъективщина. Эти варианты у тебя меняются каждые год два, как женские шляпки. А вот требование ресурсов, использование сети-тредов-диска — это объективные критерии. На них я и ориентируюсь, а не на последние веяния моды.

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

P>·>Что за "физически"? Тезис Чёрча знаешь? Любой код можно переписать на чистый, хаскель неплохо это доказывает. Вопрос — накой?!
P>Есть же пример Буравчика. Вам мало, надо к абсурду свести?
В смысле ты хочешь сказать, что его пример на хаскель не переписывается? Переписыватеся, с монадами и т.п. Но проще он не станет. Поэтому вопрос и остаётся — накой. Твои предложения "улучшить" я не понял в чём же улучшение.

P>>> Тогда приходят на помощь моки — как вы сами и сказали, замокали один компонент, что бы проверить остальные два.

P>·>Замокали "плохой", тяжёлый компонент, требующий файловой системы, сети, многопоточки, чтобы хорошо и легко проверить остальные два на саму логику и бизнес-требования.
P>В стиле ТДД — написали тесты на моках, а потом и дизайн вышел соответствующим
Нет.

P>>>Или как у Буравчика — мутабельные вычисления, кучка зависимостей да два ифа.

P>·>Ну "правильного" дизайна ты предолжить просто не смог. Т.к. это более менее реальный проект с нетривиальной бизнес-логикой. А твои штуки вменяемо работают только на хоумпейджах.
P>Какой еще "правильный"? Я продемонстрировал, что минимальными усилиями можно свести тесты к таблице истинности и хранить все это максимально близко к коду.
Я видимо не понял. Где какие таблицы?

P>·>Связывание находится в wiring code. Контроллер это просто ещё один компонент, ничем принципиальным от других не отличается.

P>Связывание и есть wiring код. Основная обязанность контроллера — именно это. И тестировать связку, как следствие.
Нет, обязанность контрллера связывать входящие запросы с бизнес-логикой. wiring code — это отдельный код для связывания компонент друг с другом.

P>Если же вы сторонник жирных контроллеров, конечно же вам придется мокать всё на свете. Не только time, а еще и sin и cos

Это твои фантазии, спорить не буду.

P>>>Ы-ы-ы — всё это придется затыкать юнит-тестами, компилер вам не помощник.

P>·>Покажи код где там какой типизации не хватает в java. И не просто общие слова, а не забывай про контекст обсуждения.
P>Я ж вам перечислил. Вы хотите еще и фичи статической типизации докинуть к нынешней теме?
Мне пофиг на общие слова и перечисления. Я прошу код. Что с чем ты собираешься согласовывать, зачем, и причём тут система типов.

P>>>blackbox — тестируем исходя из функциональных требований.

P>·>И? "текущее время" vs "данный момент времени" — это функциональные требования. Причём тут вызовы функций?
P>Вы тестируете, что результат зависит от конкретного вызова. Сами же про это и пишете.
В следующий раз когда ты мне будешь приписывать очередную фантазию, цитируй.

P>>>Вы сейчас в слова играете. Если не протаскивать зависимости, в CalendarLogic будет null во время вызова, буквально. Это значит, вы всю мелочевку повесите на dependency injection.

P>·>Что за ерунда? Откуда там null возьмётся? Что за "повесите на dependency injection"? источник времени — это зависимость CalendarLogic, инжектится через конструктор. ЧПФ, забыл опять?
P>Сами спросили — сами ответили.
Откуда _у тебя_ null берётся? У меня никаких null тут нет.

P>>>А вот вам кейс — трохи поменяли архитектуру, и БЛ начала вызываться через очередь, с задержкой от часов до дней. А время должно отсчитываться с момента когда юзер нажал кнопку.

P>·>Поменяется InstantSource, будет добывать время не из system clock, а из таймстампа в очереди. Это вообще никакое не архитектурное изменение, а одна строчка в wiring code — что инжектить как источник времени.
P>О — InstantSource появился. Значит инжектиться может не один единственный вариант с Now, а минимум два.
А ты вообще код мой читал? В первой строчке код-сниппета
Автор: ·
Дата: 28.12.23
. Ты по-моему сам со своими фантазиями споришь, я тебе только мешаю.

P>Где у вас тестирование инжекции? А ну как на проде все инжектиться будет по старому?

В прод-classpath не видны моки и вообще вся тестовая белиберда. Код не скомпилится просто, если туда правильную зависимость не вписать.

P>>>В моем случае всех изменений — вместо прямого вызова делаем отложеный.

P>·>Что за отложенный вызов? Вызов где?
P>А как вы понимаете, что такое отложеный вызов? Недавно вам непонятно было что такое dependency injection, потом выяснилось, что все вам известно.
Мне не было понятно, что ты под этим понимаешь.

P>Тут похоже вам снова надо с самим собой поговорить

Ну ссылку на вики дай.

P>>>Интересно, что может быть и так и так — часть вещей считаем сейчас, часть — потом, а время используется и там и там, как текущее, так и предзаписанное.

P>·>Ну будет два InstantSource в разных местах.
P>Вот-вот. Как вы отличите что первый и второй не перепутаны? Это ж ваша любимая тема — надо проверить, что передали то самое!
Устанавливаем в тестах, что "у нас на сервере now это 21:00:01, а на клиенте "21:00:02". И в тестах можно ассертить что в нужных случаях ожидаемое время. Что ты будешь делать со своим синлтоном Time.Now — ты так и не рассказал. Вообще я не понимаю как ты такое можешь использовать и тем более тестировать, просвети.
Что синглтоны — плохо учат ещё в детском садике, ясельной группе.

P>>>Это вам пример, как изменение дизайна вносит хаос именно в моковую часть тестов.

P>·>В моках надо будет просто сделать два источника времени, у и ассертить, что в нужных местах используется время из ожидаемого источника.
P>А еще можно обойтись без моков И свести к таблице истинности, поскольку интеграционный тест всё равно понадобится.
Ты заявил, что никак не сможешь время протестировать.

P>>>Ога — каким чудом компилер узнает, что в отложеном процессинге время в той самой функции надо брать с момента инициирования запроса, а в аудите и то, и другое?

P>·>Это будет две функции. ЧПФ, слышал?
P>Я в курсе: "ассертим что вызываем с тем параметром а не этим"
Бред.

P>·>У тебя много, на каждый сценарий.

P>Не всё так страшно — мы упрощаем не только бл, но и связывание. Посколько у вас больше одного провайдера для зависимости, всё может играть по разному в зависимости от сценария.
P>В одном сценарии у вас один провайдер времени, в другом — другой. Опаньки! Только вы это размазали по коду.
Опять фантазируешь что-то.

P>>>2 код в них тривиальный, линейный

P>·>Да, но кода этого много накопипасчено.
P>Ну и что?
Что каждую копию надо покрывать тестом. Ты правда сейчас собираешь копи-пасту продвигать как правильный дизайн?

P>>>Вы только что говорили о чудесной поддержке иде именно вашего дизайна. Покажите это чудо. Мне вот ИДЕ спокойно может заинлайнить любое из мест вызова nextFriday(now)

P>·>Круто, но зачем? Задачу-то какую решаем?
P>Затем, что признак хорошего кода это его гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить, итд, да хоть вовсе от него избавиться.
Если при этом нужно плодить копипасту, то это явно что-то не то. И неясно причём тут инлайн и гибкость кода. str.length() — это негибкий код?!

P>·>Не с любыми, а только с теми, которые тестируют детали имплементации, а не логику. Поэтому я и говорю, что _эти_ ю-тесты — не нужны. Ибо всё ровно то же придётся покрывать и-тестами с субд. Ты можешь объяснить зачем ты пишешь такие ю-тесты? Повторюсь, эти тесты не только бесполезны, но и вредны, т.к. хрупкие и усложняют внесение изменений.

P>Вы так и не рассказали, как будете решать ту самую задачу с фильтрами.
Рассказал и даже код показал,
Автор: ·
Дата: 28.12.23
насколько понял твою задачу. Если ещё остались вопросы, спрашивай.

P>>>Для того нам и нужна таблица истинности рядом с кодом и его тестами — такое можно только другими методами проверять

P>·>Дело в том, что в случае субд у тебя кода субд нет.
P>И не надо.
Надо, если хотим тестировать поведение, а не детали имплементации.

P>>>Это выхлоп билдера. С чего вы взяли, что такой код надо руками выписывать?

P>·>Это какой-то небуквальный выхлоп или что? Выхлоп какого билдера?
P>Билдера запросов к базе данных. Вы что думаете, все sql запросы руками пишутся?
Да похрен как они пишутся, хоть левой пяткой. Это всё детали реализации. Главное что они работают ожидаемым способом.

P> Кое что, для аналитики — возможно. Но это большей частью всё равно перегоняется в орм или билдер. Есть орм — можно проверять построение логики запросов орм.

В смысле ты в своих тестах linq/etc тестируешь? Зачем? У linq свои тесты есть.

P>>>Соответсвенно, и тестировать нужно соответствующим методом

P>·>Ты вроде сам заявлял, что тестировать нужно ожидания. Какое отношение метапрограммирование имеет к бизнес-требованиям или ожиданиям?
P>Запрос к базе должен соответсвовать бизнес-требованиям. Только проверяем мы это в т.ч. посредством метапрограммирования.
Что за бред? У тебя в FRD прописаны тексты запросов??! Прям где-то у вас есть бизнес-требование "select * from users where id=?" и "in: [fn1]"?! Приходит к вам клиент и говорит "а мне очень нужно чтобы out: [fn2], когда можете сделать?"

P>>>Ваша альтернатива — забить на data complexity, так себе идея.

P>·>Цитату в студию или признавайся, что опять соврал.
Цитаты нет. Т.е. соврал.

P>Вы до сих пор не привели решение кроме "ищем в бд"

Я код
Автор: ·
Дата: 28.12.23
привёл. Что не устраивает-то??! "ищем" — это "find", английский язык знаешь?

P>>>do_logic и есть или юз-кейс, или контроллер.

P>·>Я имею в виду вот этот твой do_logic. Это не юзкейс. Это код-лапша.
P>Сравнивать нужно не как выглядит, а всё цельные решения, включая моки всех сортов, dependency injection, размазаную и скрытую, тесты которые без таблицы истинности итд.
Я уже всё рассказал и показал. _Всё_. Нет ничего скрытого.

P>·>Ты читать умеешь? Я пишу "некий аспект", ты возражаешь "Все аспекты". Я где-то требовал тестировать все аспекты, кроме как в твоих фантазиях? Я спросил как тестировать один конкретный тривиальный аспект, который элементарно тестируется с моим дизайном, а ты не смог, у тебя солнце погасло.

P>У вас плохо с логикой — если нет возможности тестировать все аспекты, а это невозможно, значит в каждом конкретном случае какие то аспекты будут непротестированы.
P>Ваш тривиальный аспект это "вызываем с нужным параметром" ?
Нет. Код смотри.

P>>>·>Я топлю за вайтбокс в том смысле, что тест-кейсы пишутся со знанием что происходит внутри, чтобы понимать какие могут быть corner-cases. Но сами входные данные и ассерты теста должны соответствовать бизнес-требованиям (для данного id возвращается правильно сконструированный User). Следовательно, изменение имплементации всё ещё должно сохранять тесты зелёными.

P>>>Нет, не должно.
P>·>Должно, т.к. это тестирование бизнес-требования.
P>Должно, только ваш nextFriday поломал вообще всё, т.к. ему надо прописать другие значения, а без этого или все тесты красные, или зелены при гарантировано нерабочей системе.
Фантазии.

P>>>Смотрите про тривиальный перенос вызова из одного места в другое — ваш nextFriday поломал вообще всё

P>·>Куда смотреть? Поломал что?
P>Вам надо мок подфиксить, что бы время было той системы
Чё?

P>>>·>Вот твой код "{sql: 'select * from users where id=?', params: [id], transformations: {in: [fn1],out: [fn2]}" — таким свойством не обладает, т.к. проверяет детали реализации (текст запроса, порядок ?-парамов, имена приватных функций), а не поведение. Этот тест будет ломаться от каждого чиха.

P>>>Кое что сломается. Поскольку это метапрограммирование, то это ожидаемый результат.
P>·>Ожидаемый кем/зачем?
P>Наша задача гарантировать что запросы соответствуют бизнес-требованиям. Что бы это гарантировать, я выбрал реализацию с метапрограммированием. Вы, очевидно, выбрали другое — гонять горстку данных к базе и обратно,
Покажи пример бизнес-требования.

P>и у вас кейс с вырожденным фильром до сих пор не покрыт тестом.

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

P>>>Что бы от каждого чиха — такого нету. Запросы меняются с изменением бизнес логики.

P>·>Я тебе приводил пример — заменить звёздочку на список полей — это чих, а не изменение бизнес логики.
P>Не знаю, что у вас с этим за проблема, тут рукописные логи годами ходят в одном и том же виде, тригерят эвенты, и ничего. А гарантировать сохранность стиля генерируемого кода почему то вам кажется рокетсаенсом.
Да причём тут стиль? Это мелкий чих. Я могу переписать например "SELECT ... X OR Y" на "UNION SELECT ... X ... SELECT ... Y" для оптитмизации. У тебя все тесты посыпятся и любая ошибка — увидишь после деплоя и прогона многочасовых тестов, в лучшем случае (а скорее всего только в проде). А у меня только те тесты грохнутся, если я реально ошибусь и что-то поломаю в поведении, ещё до коммита.

P>>>В интеграционных тестах мы не проверяем точки — мы проверяем саму сборку.

P>·>Если сборка идёт в трёх точках — проверить легко. Если сборка идёт в трёхсот точках — проверить сложно.
P>Вы почемуто забываете про dependency injection — это такой код, который труднее всего покрыть тестами, а между тем у него чудовищное влияние на систему.
P>Потому и стоит менять дизайн что бы это контролировать, а через di протаскивать только технологические зависимости.
Зато этот код очень простой, линейный и на 99% покрывается компилятором. Остальной 1% покрывается стартом приложения.

P>>>То, что вы пишете — именно вайтбокс тест, тавтологичный "проверим что подставилось время клиента" — это и есть суть проблемы.

P>·>Почему вайтбокс? Тест бизнес-требования — "в ответе приходит правильное время".
P>Если это про некоторую сборку, когда вы проверяете результат, то все годится. Только вам здесь надо вбросить "время клиента" каким то раком. А потом проверить, что вбросили удачно.
Ну да, но только не раком, а моком.

P>>>Вызовем как то иначе, но обеспечим результат тот же, ваш тест сломается т.к. он по прежнему будет долбить "проверим что подставилось время клиента"

P>·>Как ты _проверишь_, что результат тот же и соответствует ожиданиям?
P>Очень просто — у нас уже есть тесты, одни должны сохраниться без каких либо изменений.
Я спросил как сделать так, чтобы такие тесты были, ты ответил
Автор: Pauel
Дата: 31.12.23
"Никак.". Т.е. тестов у вас таких просто нет.

P>Раз мы их спроектировали, а не наструячили от балды, убедились в этом, то они рассматриваются, как корректные.

P>Теперь изменение реализации должно сохранить тесты зелеными
Эээ.. ну да... верно. После любого такого изменения ни один тест не покраснеет. no matter what.

P>·>

P>·>·>Как ты тестируешь что построенный запрос хотя бы синтаксически корректен?
P>·>тот самый интеграционный тест, на некорретном запросе он упадет
P>·>

P>·>Т.е. у тебя есть туева хуча ю-тестов для каждого сценария, которые проверяют _тексты_ запросов, но для проверки, что текст запроса хотя бы корректен — приходится ещё и и-тестом каждый запрос проверить для ровно тех же сценариев.
P>интеграционные тесты строятся по функциям, а не по запросам к базе.
Я о чём и говорю. В каких-то случаях у тебя могут меняться запросы. Ты уже сам говорил о случае с пустым значением, которое ты заявил, что и-тестами тестировать не собираешься.
И это могут быть нефункиональные требования, а какие-то технические детали, оптимизации. Простой пример. Скажем, если идёт выборка по небольшому списку параметров, то строится запрос с "IN (?,?)", иначе создаётся временная табличка и с ней джойнится.

P>А вот запросы к базе вещь слишком сложная и ответственная, потому стоит использовать разные инструменты, а не рассказывать, что вам запретили метапрограммирование

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

P>>>Для юнит-тестов это как раз нормально, их предназначение — тестировать всю мелочевку, из за которой у вас вообще что угодно можно происходить

P>·>Дело не в мелочёвке-большичёвке, а в том, что ты тестируешь код как написано, а не ожидания/бизнес-требования. Ровно то, чем ты меня пытался попрекать.
P>Похоже, что вам действительно ктото запретил метапрограммирование
Фантазируешь.

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

P>·>Мрак.
P>Видите — не так, как вы привыкли и сразу ужос-ужос-мрак-и-смэрть
Ну вроде это уже вс проходили и забили. Заталкивать тесты в прод-код... зачем??!

P>>>И как же вы решаете проблемы с data complexity которая тестами не решается используя исключительно косвенные тесты?

P>·>А конкретнее? С примером кода.
Именно. Примера кода я так и не дождусь.

P>Это я вам вопрос задал, вообще говоря. Те самые фильтры — вполне себе хороший пример.

Для тебя — хороший. Потому что у тебя оно в голове, т.к. ты с этим трахаешься последние пол года, не меньше, похоже. Я вообще плохо понимаю о чём идёт речь и с какими конкретными проблемами ты столкнулся. Я ведь не телепат... А подробностей от тебя не дождёшься. Приведи код, сформулируй проблему, я может и погляжу подробнее.

P>>>Это выхлоп билдера — в нем связывание, конверсия, маппинг и тд. Т.е. у нас кейс с метапрограммированием — результат фукнции это код другой функции. И я использую ровно тот же подход что и везде — сравнение результата. Только в данном случае наш результат это код.

P>·>Зачем нам тестировать выхлоп билдера? У билдера должны быть свои тесты.
P>Это и есть тесты билдера. Только конкретного, в котором заложено много различных вырожденных кейсов. linq не в курсе, что вы вытаскиваете всю базу, ему по барабану. Мне — нет.
Linq выдаёт список результатов — вот и проверяй, что в этом списке — вся база или не вся.

P>>>Например — фильтр вырождается в true. А вот если мы накладываем ограничения на результирующий запрос, у нас эта часть исключается принципиально.

P>·>Не понимаю проблему. Если фильтр выродится в true, то субд вернёт те записи, которые мы не ожидаем, тест провалится.
P>Ну так найдите эту комбинацию. А их, мягко говоря, немало. Как будете искать?
Анализом требовний, кода, данных, етс. Обычный corner case analysis. А как вы будете искать?

P>>>Простой вопрос был — как ваше доказательство проверит само себя если другой коллега начнет писать код как ему вздумается?

P>·>Верификатор выдаст ошибку. Почитай про какой-нибудь coq что-ли.
P>И вы так прямо и втащите coq в любом проект на джаве?
Нет. А вы таки втащили доказательное программированипе в js?

P>·>Ну будет в коммите одном из десятка тестов вырожденный фильтр... И? Особенно весёлые штуки типа "A = B OR A <> B" из-за немножко не так поставленных скобочкек. Ты как равьювер будешь смотреть тесты и в уме интерпретировать sql запросы?

P>Еще раз — фильтр не хардкодится, а строится.
Я потерял суть. О чём речь вообще? Пример в студи. КОД!

P>>>Наоборот. Руками затейники выдают такое, что ни один генератор проглотить не может. А если вы начинаете с аннотаций или хотя бы интерфейсов, то уж точно ясно, что это реализуемо.

P>·>Верно, но называется это code-first со всеми последствиями.
P>Нет, не называется. Разница между code-first и design-first не формате и расширении файла, как вам кажется, а в очередности деливери.
Верно. В design-first деливерится манифест first. А не http-приложение, генерящее манифест.

P>В том то и дело — ваша задача накидать интерфейсы-модели так, что бы имплементации было 0. Тогда можно деливерить сразу, а вот будет ли деливериться имплементация, и когда — уже дело десятое.

Ну да. Откуда же берутся конфиги/тенанты/http-запросы?

P>Посмотрите grapql, grpc — API и есть код, вопрос в том, что вы деливерите. Полурабочий каркас приложения — это code-first. А если описание, которое могут взять другие команды, и работать независимо — design first

Расскажи где в graphql/grpc можно найти конфиги/аннотации/переменные окружения?

P>>>Вы вместо вопроса слишком злоупотребляете телепатией. АПИ, если отдаётся, то в большинстве случаев уже ничего генерить не надо. Например, согласовали интерфейс, зафиксировали, и каждый пошел работать над своей реализацией.

P>·>Ты опять врёшь. Не телепатией, а из твоих слов: "Манифест генерируется по запросу, обычно http", противоречие выделил жирным. Или ты похоже просто в прыжке переобуваешься.
P>Вы вместо уточнения пытаетесь в телепатию играть. Если у нас готовое приложение, то конечно же генерируется — и странно было бы иначе делать.
Угу. ЧТД. "готовое приложение" == "code-first".

P>А если у нас ничего нет, только АПИ, то пишите АПИ сразу так, что бы быстрее его заделиверить и цикл разработки был максимально короткий.

Откуда в апи у тебя возьмутся тенаты? Или ты просто словоблудием занимаешься? Когда удобно имеешь в виду gprc, а когда удобно вкидываешь про готовое приложение — лишь бы собеседника переспорить.

P>>>Вы похоже не вдупляте — design-first = вначале деливерим АПИ, а потом уже реализация, куда входят переменные, конфиги, логика тенантов итд.

P>·>http-сервер это такой АПИ... Угу-угу.
P>Я вам про интерфейсы, а вы сюда http-server тащите. Дурака валяете.
Не ври. http втащил ты. Цитиую: "по запросу, обычно http".