Здравствуйте, Pauel, Вы писали:
P>>>Логику, вычисления мы тестируем по таблице истинности. И чем меньше дополнительного кода сверх этой таблицы истинности, тем лучше. А вам надо все поразмазывать по мокам
P>·>Ну тестируй. Моки тебе ничем не мешают.
P>Мешают — хрупкие тесты, т.к. вайтбоксы, и дополнительный приседания что бы обмокать зависимости
Хрупкость тестов с моками — это твои фантазии и страшилки. Конечно, в том виде как ты показывал способ использования моков — делает тесты хрупкими. Просто не используй моки так.
P>>>Функционально — ровно столько же.
P>·>Нет. Он у меня ещё и содержит склейку. У тебя склейки нет.
P>Нету такой функции как склейка. Вы все еще думаете вайтбоксом.
Частичное применение функции — даёт функцию.
P>>>Моки не тестируют интеграцию. Моком вы можете проверить, а что будет, если подкинуть ту или иную зависимость. А нам надо совсем другое — готовый компонент работает как нам надо.
P>·>Ты читать умеешь? Ты где увидел в моём предложении выше слово "моки"? Хинт: моки вообще ничего не тестируют. Тесты — тестируют.
P>Тесты на моках интеграцию не тестируют, и не могут. Тест интеграции — проверка функционирования в сборе, а не с отрезаными зависимостями.
Я не знаю к чему ты это всё рассказываешь, с чем споришь. Впрочем, тоже слишком общее, поэтому неверное утверждение. Можно тестировать интеграцию двух компонент, мокая третий. Например, можно тестировать интеграцию контроллера с бизнес-логикой, замокав репу.
P>>>Количество интеграционных тестов моки уменьшить никак не могут. Моки — это фактически понижение уровня тестирования за счет обрезания зависимостей. Нам же надо не понижение, а повышение.
P>·>Могут, т.к. большинство логики тестируется ю-тестами. А и-тесты нужны только чтобы тестировать интеграцию уже протестированных компонент.
P>Вот у вас снова из за моков почему то интеграционных тестов становится меньше. Вы путаете два противоположных случая — тест со включенными зависимостями и с обрезанными.
Моки позволяют упростить дизайн, снизить тяжеловесность тестов, уменьшить количество интеграционного кода, и соответственно — меньше кода — меньше тестов.
P>>>Есть метод — его нужно вызвать в тестах. И у вас будет ровно то же. Просто вас тянет тестировать код, а не ожидания вызывающей стороны.
P>·>У меня такого метода просто не будет. Следовательно и тестов писать просто не для чего.
P>Куда ни ткни у вас "таких проблем у меня не может быть".
Потому что для данной конкретной проблемы существует подход, чтобы эта проблема не возникала. Если ты не хочешь использовать этот подход, тебе приходится бороться с этой проблемой.
P>>>Вместо проверки конкретной склейки делаем интеграционный код линейным, буквально, а интеграцию осуществляем на самом верху.
P>·>Чем выше, тем больше мест. Функция nextFriday() осуществляет склейку у себя внутри. И покрывается одним тестом. У тебя будет nextFriday(now) — и на самом верху будет множество call site для этого накопипастено. Код линейный, да, но накопипастен в кучу мест.
P>Нужно проверять все эти call site вне зависимости от способа доставки депенденсов, каждая функция должна быть вызвана в тестах минимум один раз.
Проверять на что и каким тестом? В call site я могу использовать мок nextFriday() и проверять поведение ю-тестами.
P>>>Простите, вы мапперы из одной структуры в другую как тестировать собираетесь?
P>·>В ю-тестах — по частям. Иначе изменение логики маппинга одного поля сломает все deep.equal тесты, где это поле есть.
P>Вы наверное не в курсе, то в джаве типизация хилая — не позволяет внятно типизировать согласованые значения, например "если ид = 0" то "created = null"
P>А раз так, то вы будете выписать такие согласованые значения руками.
P>Следовательно, и тестировать нужно ровно так же, а не размазывать значение по разным тестам
P>Будь у вас фукнциональный язык, можно было бы типизировать чуть не все подряд, но у вас такого фокуса нет.
Не очень понял. Наверное мы говорим о разных вещах. Покажи код где там какой типизации не хватает в java.
P>>>Пусть используется. Что вас смущает ?
P>·>Что в каждое из этих мест тебе понадобится копипастить склейку nextFriday(now) с источником текущего времени и покрывать это ещё одним тестом. Хотя, как я понял, ты вообще заявил, что такое протестировать ты просто не сможешь.
P>Вы кроме как вайтбоксом думать не хотите. "тестировть склейку" — это тупой вайтбокс, со всеми его недостатками. Вайтбокс в мейнтенансе дороже блакбокса. Вам это каждая ссылка в гугле скажет.
P>Не надо тестировать склейку — нужно тестировать результат той или иной функции. И тут по барабану, что откуда вызываете — есть функция, её нужно связать с таблицей истинности и покрыть тестом.
P>Вы же все время намекаете, что ваши моки от этого избавляют.
Так я это и тестирую результат, что он зависит от nextFriday() правильным способом. Разговор о другом.
nextFriday(now) — это функция возвращающая пятницу для данного момента времени. А nextFriday() — это функция возвращающая пятницу для текущего момента времени. В первом случае мы по месту использования должны откуда-то брать некий момент времени, а для второго — мы знаем, что это текущий момент.
В функциональных яп — это частичное применение функции. У нас просто разная семантика функции "текущее время" vs "данный момент времени". Причём тут white/black — вообще неясно.
P>·>Ещё раз. nextFriday() ≡ nextFriday(now) ∘ timeSource.now(). Если ты отказываешься использовать в call site готовый и протестированный nextFriday(), значит тебе в каждый call site придётся протаскивать timeSource и копипастить композицию с nextFriday(now). Это увеличивает кол-во прод-кода и требует больше более тяжеловесных тестов для покрытия всей копипасты.
P>Вам точно так же нужно протаскивать timeSource, только без параметра. И тестировать нужно ровно столько же, т.к. нужно знать, как ведет себя вся интеграция разом.
Не нужно его протаскивать. Он уже "частично применён" в CalendarLogic. Ты вообще понимаешь что такое ЧПФ?!
P>>>Читайте себя "Так функция может использоваться из множества мест."
P>·>И?
P>Вы же все эти места обмокали. Вот новый девелопер пришел на проект, откуда ему знать, должно его изменение повлечь правки моков или нет?
Компилятор расскажет.
P>·>Эээ.. Ты рефакторингом точно умеешь пользоваться? Компилятор и IDE всё покажет, подскажет и подправит.
P>Вы здесь жульничаете — по факту вы мне задали тот же вопрос, что и я вам. Но у меня, по вашему, будут проблемы и ни компилер, ни иде не помогут, а вот вам в аналогичной ситуации будут помогать
P>Я же вижу, что с чистыми функциями помощи от компилятора кратно выше
P>В некоторых случаях кода писать больше, за счет того, что в других — намного меньше, и бОльшую часть можно переложить на компилятор и иде.
P>Например, нужно заинлайнить функцию
P>с чистой функцией это нажал на кнопку и получил результат
P>с вашими зависимостями компилер говорит "приплыли" потому что в текущий блок зависимость не подложена, и компилер не в курсе, как правильно её привязать.
Ровно то же и с частично применёнными функциями.
P>Ровно так же дела с переносом кода куда угодно, выделение наследника, сплющивание иерархии, конверсии класса в функцию и обратно
P>А вам всегда будет мешать та самая зависимость
Вам зависимость тоже будет мешаться всегда, просто в других местах.
P>Если вы не согласны — покажите как ваша иде инлайнит код nextFriday по месту каждого из call site. Явите же чудо, про которое вещаете тут годами
А ты можешь заинлайнить, например, хотя бы str.length()? Или тоже "тапочки не приносит"?
P>>>·>Ты потерял контекст. Тут шла речь о ю-тест упоминал "а что если в колонке пусто". В колонке, внезапно окажется, будет не пусто а дефолтное значение из бд. Багу такую поймаешь только на проде, т.к. и-тесты для таких подробных мелочей явно писать не будешь.
P>>>Наоборот — именно для таких мелочей юнит-тесты и нужны.
P>·>Ты не читаешь, что тебе пишут.
P>Наоборот. Я как раз для подробных мелочей и пишу юниты-тесты. Вероятно, вы делаете иначе.
Ты не понял, что я тебе написал. Попробую объяснить ещё раз.
"а что если в колонке пусто" — это не реальное возможное ожидание, а тестовые параметры, которые ты подсовываешь своей "чистой функции" в своём ю-тесте. А на самом деле никакого "в колонке пусто" может просто не быть, т.к. dbms заменяет его на дефолтное значение. Твой ю-тест тестирует "как написано в коде". И эту "подробную мелочь" ты тестировать в и-тесте не собираешься, а значит будешь ловить баг на проде.
P>>>Что там будет с бд, когда другой девелопер чего нибудь фиксанет — а хрен его знает.
P>>>А если таблица истинности рядом с тестами и тестируемой функцией, шансов намного больше что проблему заметят еще во время код ревью.
P>·>Нет, не пытаюсь, ты опять фантазируешь. Я пытаюсь донести мысль, что надо проверять что код делает, а не как он выглядит.
P>Еще раз — у меня нет буквально в коде того или иного запроса целиком, а есть конструирование его.
Цитирую тебя:
Что вас смущает? deep.eq проверит всё, что надо. query это не строка, объект с конкретной структурой, чтото навроде
sql: 'select * from users where id=?',
Это не буквально?!
P>Я ж прямо пишу — построение запроса по фильтрам.
P>+ шаблоны запроса, шаблоны фильтров итд
P>То есть, у нас в наличии обычные вычисления вида object -> sql которые идеально покрываются юнит-тестами
То что они идеально покрываются ю-тестами, не означает, что их надо покрывать ю-тестами. Цель тестирования репо не то, как запросы выглядят, какие буковки сидят в твоих шаблонах фильтров, а то что выдаёт субд для конкретных данных.
P>>>Интеграционный тест проверяет всю интеграцию разом, а вас тянет проверить каждую строчку. Каждая строчка — это про юнит-тестирование.
P>·>Твой один интеграционный тест проверяет интеграцию одного do_logic. Таких do_logic у тебя будет дофига, и каждого надо будет проверить.
P>Этот do_logic по одному на юз кейс. Сколько юз кейсов — столько и таких методов, а значит столько будет и интеграционных тестов. И у вас будет ровно то же. И никаие моки этого не отменят.
P>Даже если вы вообще удалите зависимость от Time.now, это ничего не изменит — количество интеграционных тестов останется прежним
А у меня этих твоих do_logic — по нулю на юз-кейс. Чуешь разницу?
P>>>·>Именно. Т.е. усложняешь прод-код, теряешь возможность протестировать и ловишь баги в проде. Нам такого не надо.
P>>>Забавная телепатия.
P>·>Никакая не телепатия. Если ты никак не можешь протестировать, то проблемы будут обнаруживаться в проде. Ок, возможно, ещё при ручном тестировании.
P>Я и говорю — телепатия. Вы лучше меня знаете, что у меня в коде, что у меня за проект, где больше всего проблем возникает, сколько должен длиться билд
Ты вроде прямо заявил, что некий аспект поведения ты не можешь протестировать. И время билда ты сам рассказал. Я телепатией пользоваться не умею, могу привести твои цитаты.
P>·>Не следует.
P>Именно что следует. Вы топите за вайтбокс тесты, а у них известное свойство — поломки при изменении внутреннего дизайна.
Я топлю за вайтбокс в том смысле, что тест-кейсы пишутся со знанием что происходит внутри, чтобы понимать какие могут быть corner-cases. Но сами входные данные и ассерты теста должны соответствовать бизнес-требованиям (для данного id возвращается правильно сконструированный User). Следовательно, изменение имплементации всё ещё должно сохранять тесты зелёными.
Вот твой код "{sql: 'select * from users where id=?', params: [id], transformations: {in: [fn1],out: [fn2]}" — таким свойством не обладает, т.к. проверяет детали реализации (текст запроса, порядок ?-парамов, имена приватных функций), а не поведение. Этот тест будет ломаться от каждого чиха.
P>У вас выходят вайтбокс тесты без недостатков свойственных вайтбокс тестам.
P>Чудо, которое можно объяснить только вашими фантазиями
Допускаю, что я неправильно терминологию использую white/black box...
P>>>Обычное правило — 1 инструкция на строку. Вы втюхали минимум две.
P>·>И что? Небо на землю упадёт?
P>Выглядит так, что вы отрицаете недостатки моков и вайтбокс тестов, даже посредтством такой вот дешовой детской подтасовки.
Я отрицаю недостатки, которые ты придумал, основываясь на непонимании как надо использовать моки.
P>·>Ну ввести константу — не проблема.
P>Её нужно вводить, хотя бы для того, что бы обеспечить согласованность некоторых тестов.
P>А все лишние из тестов нужно убрать, что бы ничего не мешало делать ревью
Это всё мелочи жизни, применимо к любому коду. В любом коде хорошо вводить константы, если они означают одно и то же в разных частях кода. Любой код надо делать так, чтобы было проще читать/ревьювить. При чём тут сабж?
P>Чем больше, тем лучше. Если моки можно убрать — их нужно убрать
Это уже твои личные заморочки от неумения пользоваться моками.
P>>>Вы забыли, что именно вы претендуете что решите это всё тестами.
P>·>Опять фантазии. Давай в следующий раз когда ты решишь что я думаю, на что я претендую, и т.п., подкрепляй моими цитатами.
P>Вы же пишете, что вам на проверку любой сложности фильтров хватит одной тестовой бд с ничтожным количетсвом записей.
Нет, ты врёшь, я такое не писал. Ты опять приписываешь мне какие-то свои фантазии, вместо прямой моей цитаты.
P>·>Не знаю где ты собрался размазывать и запихивать таблицы, просто не делай так, я разрешаю. Но это не отвечает на вопрос, куда делся человеческий фактор-то?
P>Если вы таблицу истинности размазали по бд, вам нужны административные меры, что бы заставить девелоперов собирать всё в кучку на код-ревью или для разработки.
Опять фантазии. Я не размазывал таблицу истинности по бд.
P>>>Интеграционные тесты не проверяют варианты склейки — они проверяют функционирование в сборе. Юнит-тесты проверяют работоспособность примитивов.
P>·>У тебя плодится много точек сборки, которые теперь приходится проверять.
P>Проверять нужно не точки сборки, а всю сборку целиком. Точки сборки — та самая вайтбоксовость с её недостатками, которые вы отрицаете уже минимум несколько лет
Ок, переформулирую. В сборке целиком тебе нужно проверять все точки . Тебе надо будет проверить, что где-то в твоём склеивающем коде do_logic_742 в качестве now в nextFriday(now) подставилось время сервера, а не клиента, например. И так для каждого do_logic_n, где используется nextFriday.
>> Ты решил терминов наизобретать? Что такое "работоспособность"? Чем она отличается от функционирования?
P>Функционирование и работоспособность это слова-синонимы. В руссокм языке это уже давно изобретено.
Тогда неясно зачем тебе и юнит тесты, и интеграционные, если и те, и те проверяют функционирование, и что тебе в твоих и-тестах приходится проверять всю функциональность.
P>>>Не из кода, а из требований вызывающей стороны.
P>·>Что за требования вызывающей стороны, которые диктуют, что должен быть написан код с fn2, а не fn3? Вызывающая сторона это вообще что и где?
P>Например, те самые требования. Если нам вернуть нужно User, то очевидно, что fn2 должна возвращать User, а не абы что.
Ерунда какая-то.
Во-первых, это очень слабая проверка, т.к. такое работает если только у тебя _каждая_ функция имеет уникальный тип результата.
Во-вторых, вспомним код:
const getUser = repository.getUser(id);
const oldUser = db.execute(getUser);
const newUser = bl.modifyUser(oldUser); // вот тут у тебя должен быть compilation error, если getUser будет возвращать что-то отличное от User.
Ты своими тестами компилятор тестируешь что-ли? Или у тебя настолько "типизация нехилая", что каждый тип приходится тестом покрывать?!
P>>>Это ваша фантазия — эдак у вас юнит-тесты станут code smell, тк они принципиально по своей сути видят слишком много.
P>·>У нас юнит-тесты видят те же публичные методы, что и используются из других мест прод-кода. Тестировать то, что видно только тестам — это и есть твоё "как написано", завязка тестов на детали внутренней реализации.
P>Вы снова додумываете вместо того, что бы задать простой вопрос.
Вопрос о чём? Ты сам сказал, что fn2 видно только для тестов. Цитата: "fn1 и fn2 должны быть видны в тестах, а не публично". Я просто пытаюсь донести мысль, что это code smells, т.к. тесты завязаны на непубличный контракт компонента, иными словами, на внутренние детали реализации.
P>·>Я не знаю что такое содержимое тестовой бд и накой оно вообще нужно.
P>Во первых, врете, см ниже
Мимо.
P>Ну как же — вы собирались фильтры тестировать посредством выполнения запросов на бд, те самые "репозитории" которые вы тестируете косвенно, через БД.
Да.
P>Или у вас фильтрация вне репозитория сделана?
Нет.
P>>>Вероятно, это особенность вашего проекта. Любая более-менее внятная статистика-аналитика по бд это довольно сложные алгоритмы, и здесь нужно чтото лучше, чем смотреть в бд и думать, что же должно быть на выходе.
P>·>Верно. Я уже показал что лучше — сохранить данные в бд и поискать их разными фильтрами.
P>А вот и доказательство, что врете — здесь вы знаете что такое содержимое бд и накой оно нужно.
С т.з. тестов репы нет никакого "содержимого тестовой бд". "сохранить данные в бд" == вызов метода save репы, "поискать их разными фильтрами" == вызов метода find. Я же в пример кода уже дважы тыкал. С т.з. теста впрочем и бд как таковой тоже нет, можно сказать, есть просто класс с пачкой методов, которые тест дёргает и ассертит результаты.
P>>>Раскройте глаза — здесь блекбокс. Выборочно вызываем бд нашим кодом, проверяется весь пайплайн в сборе, косвенно. Только нам не надо проверять всё на свете таким образом, т.к. есть и другие инструменты.
P>·>"я знаю, как строятся фильтры" — это и есть whitebox, не лукавь.
P>Как строятся — какой должен получиться в итоге запрос.
P>А вот подробности того, какого цвета унутре неонки(связывание, конверсия, маппинг, итд) — мне не надо.
Но вот тут "params: [id], { [fn1],out: [fn2]}" ты именно это и тестируешь. Ты уж определись надо оно тебе или не надо... Или ты тестируешь то, что тебе не надо?!!
P>Чем сложнее логика построения запросов — тем больше тестов придется писать. Косвенные проверки не смогут побороть data complexity
Ну да. Больше логики — больше тестов. Причём тут "косвенность" проверок?
P>>>Вы никак не можете объяснить преимущества размазывания таблицы истинности по тестами и бд, чем это лучше хранения таблицы истинности рядом с кодом и функцией с тестом.
P>·>Размазывание таблицы истинности придумал ты сам, сам и спорь со своими фантазиями.
P>Ну как же, смотрите выше — "лучше — сохранить данные в бд и поискать их разными фильтрами."
И? Я код же показал. Где там размазывание чего?
P>>>Затем, что доказательство корректности — это необходимое условие. А такое доказательство + тесты + код ревью, необходимое и достаточное.
P>·>Если у тебя есть доказательство корректности кода, то тесты уже не нужны. Это и есть идея доказательного программирования.
P>Ну и дичь Каким чудом ваше доказательство сработает, если в код влезет другой ?
P>Дональд Кнут на линии: "Остерегайтесь ошибок коде; я только доказал его правильность, но не проверял его."
Ты либо крестик, либо трусы. Док.программирование это спеки, контракты и верификаторы. Тесты тут непричём.
P>>>Вы не в курсе, ни что за проект, ни какие требования, итд. Далеко не все проекты могут протаскивать изменения в прод прям сразу.
P>·>Я знаю, но это обычно недостаток, а не повод для гордости.
P>А еще чаще это признак огромного проекта
Огромного проекта в плохом состоянии, да. Впрочем, не раз наблюдал и маленькие проекты на неск. сот строк билдятся час — сразу видно, писали по всем канонам и заветам дядек из интернета!
P>·>Вопрос в том, что является источником — прога на конкретном яп или language agnostic описание контракта.
P>language agnostic описания, если вы про json, это отстой, т.к. удлинняет цикл разработки — есть шанс написать такой манифест, который хрен знает как заимплементить
Написать такой корявый манифест немного сложнее, чем сгенерить.
P>Такое описание далеко не всегда нужно:
P>1. например, у нас всё на джаве — джава, скала, котлин, итд.
P>2. например, у нас нет внешних консумеров
P>3. например, мы связываем части своего собственного проекта — здесь вообще может быть один и тот же яп
Тогда нафиг и не нужны аннотации, и уж тем более манифесты.
P>·>Не десятое, а первое. Погляди в словаре что означает слово "first".
P>design-first означает, что в первую очередь деливерится апи, а не абы что. А вот как вы его сформулируете — json, idl, grapql, grpc, интерфейсы на псевдокоде, java классы без реализации — вообще дело десятое, хоть plantuml или curl.
P>Здесь самое главное, что бы мы могли отдать описание всем, кто будет с ним взаимодействовать. А уже потом начнем работать над реализацией.
Так у тебя же в начале (first) пишется/меняется code и потом из него генерится описание, которые ты отдаёшь всем. Да ещё и разное описание, в зависимости от фазы луны. Т.е. ты в первую очередь кодишь свои переменные, конфиги, логику тенантов, и лишь потом у тебя появляется описание.
P>>>Я ж сразу сказал — апи уже есть, а реализации нету, зато можно и тесты писать, и клиентский код, и документацию прикручивать
P>·>А начался разговор, что у тебя аннотации хитрые и сложные, зависят от переменных окружения и погоды на марсе.
P>Основа апи остаётся. Переменные окружения и конфиги не могут поменять параметры метода, типы, результат итд. Но могут скрыть этот метод, или добавить метаданные, например, метод начнет требовать авторизацию, или наоборот, будет работать без авторизации.
И на чём эта вся логика написана? На grpc, да?