Navigated to #70 Дебаты: юнит тесты против интеграционных с Александром Макаровым - Transcript

#70 Дебаты: юнит тесты против интеграционных с Александром Макаровым

Episode Transcript

Друзья, привет. Это подкаст Организованное программирование. Я его ведущий Кирилл Макевнин. Сегодня у меня в гостях Александр Макаров, широко известный в PHP кругах человек, коразработчик фреймворка, который я всю жизнь не могу произнести. То, то и, то ещё как. Вот, который нам очень-очень долго обещал переход третью версию. И я, поскольку за этим немножко не слежу, но только сейчас понял, что третья всё-таки уже в продакшене зарелизилась и можно и пользоваться. Да, Саша? Да, всё так. На самом деле пакеты зарелизились практически все. Последние релизы были вот прямо в декабре. И мы документацию тоже красивый рендернход сделали на днях и всё это дело собираем в лендинг. Скоро будет висеть на сайте прямо совсем красиво, потому что я помню в первые годы того, как я начал заниматься разработкой, я начинал с PHP и как раз плюс-минус и тогда появился. И он тогда был такой типа после кей всяких, помнишь, там кейк ещё какие-то были фреймки. Он такой был таким свежим, классным. Мы ещё рельсу тогда не знали. И я помню, у меня были такие баттлы типа типа какой фреймворк использовать. Мы тогда выбрали Zфрейвоorkк как бы посчитав, что он ближе, так сказать, к ну такой надёжней, потому что он ближе, знаешь, к организации. Вот. Но в итоге ZН фреймворка нет, хотя там были попытки. А и до сих пор живой, развивается и вот существует. Хотя, конечно, к сожалению, наверное, он немножко уступает по популярности Ларавелю и там Симфони, но очень круто, что вы столько лет как бы делаете, движетесь и вообще занимаетесь этим. Не все выдержат. Сколько лет ты уже Сашем занимаешься? Ой, да, уже очень много. Это с 2008, кажется, года. Капут вообще. Выдержка, конечно, сумасшедшая. Такое, как бывает, когда, да, люди меняют работу по одному раз там в год и у тебя там одним и тем же фреймворком там сколько? 17 лет получается уже 18%. Да. Ну да. Но я же занимаюсь не как таким основным видом деятельности. Это такой не очень коммерческий продукт, не очень коммерческий проект, по крайней мере, пока. И я делаю коммерческие проекты тоже. И вот их я меняю. Всё основная работа. Всё как у всех, да. Но тема нашего занятия сегодня, ребята, не не обсуждение фреймворка, а сегодня у нас будет разговор идти про тесты. Я эту тему люблю, вы знаете, периодически на подкасте про это рассказываю. Кстати, кто не видел про ТД мм наш разговор предыдущий, а, посмотрите обязательно, там много чего полезного, интересного. Мы решили продолжить, потому что одно дело ТД, а другое дело в целом разговор про то юниты или интеграционные тесты, потому что, а, дихотомия такая существует, кто-то тянет больше за одно, кто-то за другое. Со временем это немножко менялось. Я нахожусь в лагере те, кто больше про индекционные тесты. А Саша сегодня выступает в лагере, кто больше за юниттесты. У нас будет сегодня попытка провести дебаты на эту тему. Мы посмотрим код. Мы немножко тут репозиториев тоже подготовили. Поговорим вообще в целом об этой теме. И, э, вообще, кто знает там, следит за моим каналом, на который, кстати, надо подписаться в Телеграме и на Сашин канал сразу, чтобы уж далеко, да, не вставать два раза с дивана, на его канал тоже подпишитесь. Короче, в Телеге, я когда про это писал недавно, то, конечно, как и раньше, в целом всегда тенденция, что людей, которые любят юниты, по крайне мере среди тех, кто комментирует, их, как правило, больше, но, видимо, потому что те, кто любит больше интеграционные, они как бы такие со статьёй согласны и, в общем-то, не комментируют. А вот те, кто любит юниты, комментирует. Поэтому, по крайней мере, по комментариям всегда складывается ощущение, что людей, которые тяготеют вот к Сашной позиции, их больше. Вот. А по факту так это или не так, я не знаю. В общем, главное, поставьте лайк видео, если так, дизлайк, если не так. Мы потом посчитаем и посмотрим. Итого, дихотомия. А что лучше больше писать и по какой причине? Мы, кстати, не говорим про до кода или после кода, неважно в нашей текущей дискуссии. Мы говорим вот о том, когда и как писать. Я бы начал немножко разговор с попытки хотя бы чуть-чуть по терминам пройтись, потому что м с терминами вообще войти довольно плохо. Мы все знаем с вами, ребят, несмотря на то, что термины вроде как бы есть, но каждый раз, когда ты начинаешь говорить, понимание абсолютно противоположное у всех по разным вещам. И действительно формальных определений, ну, честно говоря, мало. Вот в каких-нибудь алгоритмах много, в каком-нибудь чистом компьютерса там много формальных терминов, а вот в инженерии, э, почти любой термин можно перевернуть с ног на голову. И восприятие у всех очень сильно разное, да? Давай я начну с юнитов, чтобы сразу, так сказать, вкинуть вот эти противоречия, которые существуют. Когда говорят про юниты, часто ещё говорят модульные тесты, то обычно говорят, это там тестирование модуля, ну, и приводят пример класс или там функция. И тут сходу можно сказать, что ну, допустим, у нас есть функция, которая делает HTTB запроса или уходит в базу. класс, да, модуль там, да, функция, да, может быть одна простая функция, но она при этом входит в, собственно, во внешнюю систему. Вот в тво в твоём представлении ты назовёшь такую функцию, тест на такую функцию юнитом или нет? Ну, во-первых, надо поправиться, что такое модуль. Угу. Потому что модуль - это не класс и не функция. Это просто набор чего-то, что между собой связано и изолировано. Это модуль. Ну это вот то, что называется модулем в всяких разных умных книжках у Мартина и так далее. Он не говорит, что модуль - это класс или модуль - это функции или модуль - это сервис. Он говорит, что модуль - это вот какая-то куча чего-то, что работает вместе. И соответственно, а юнит-тесты опять же модульными называются, а и просто так, и и не просто так, потому что они этому не всегда совсем соответствуют. Вот, как правило, unit - это про тестирование самого кода. Это не про тестирование, не знаю, там, вот алгоритма, в общем, который там вот юзер заходят в начало и выходит в конце опять юзер. Это не то. То есть модуль - это вот программный модуль, и он проверяется. Кто-то проверяет по-разному. Вот я лично большой противник того, чтобы тестировать отдельные, а, не знаю, там классы, методы, ещё что-нибудь, если, конечно, эти классы отдельные или методы не используются, а, как публичная IP. Я за то, чтобы тестировать только публичные IP всегда интерфейс, чтобы не не лезть в чёрные ящики никогда. Ну, по возможности. Сейчас, сейчас очень смешно получается, потому что по сути при таком определении для многих, я уверен, что кто тебя сейчас послушает, они такие: "Ты же говорил, что ты, знаешь, как сечёшь в этой теме". В том смысле, ты же говорил, что ты за юниты, потому что я точно знаю, как многие люди действительно связывают это именно вот с уровнем прямо классов, с уровнем обычных функций, потому что, ну, не везде есть классы, да, ну, допустим, вот язык, в котором только функции, и получается, что тест на функцию, по идее, по определению, модульный, если у тебя есть функция, которая сама по себе живёт. Ну, давай не модульная, а именно unюниттест, да, потому что она сама по себе живёт, она часть публичного апи или нет? Давай любая математическая функция, да, давай начнём вот действительно с простейших примеров. функция, которая вычисляет tangс. Вот прекрасно. Это это это публичная программная IP. Угу. Угу. То есть эту функцию прекрасно тестировать это именно тем, что называется unюниitтест. Это нормально, да? Но он юнит-тестом становится. Почему? По это же дело не в самом тесте, да? Это дело в, собственно, в самой функции. Да, да, да. Потому что эта функция, она даёт публичные апе, которая используется много где. Угу. Ну, в принципе, используется кем-то, значит, мы его можем протестировать. О'кей. Теперь тогда вопрос такой. Я знаю, что существует мнение, когда, допустим, берё берётся какая-то опять же модуль, да, давай вот модуль может что называться? Вот мы сделали некую внутри ээ нашего приложения подсистему, и это подсистема, например, плагинов или это подсистема работы с базой данных. Ну вот мы сами, мы не любим ORM, и мы вот такие накидали там какой-то простики Qybilder какие-то DAO, шма там, вот это всё там, ну, три-четыре классика примитивных, да. Я знаю, что есть часть людей, которые при тестировании этой фигни, ну, допустим, они её тестируют, и они, э, такие: "О'кей, но в базу ходить не надо, потому что вроде как бы неправильно по, ну, там мы начитались фаулера и всех остальных, и говорят, что это неправильно. Давай всё это замокаем и положим всё это в папочку юнит-тестов и будем считать, что это юнит-тесты". Вот. Ты согласен, что это юнит-тест или нет? Ну, в принципе, это похоже на unтеest. То есть, по-хорошему, я, если уж так смотреть, то по-хорошему надо всё это дело тестировать так, чтобы оно ещё и в базу ходило. То есть, ну, как бы весь вот этот вот модуль наш, а если у него не м предусмотрена смена бэкэнда там на меory или ещё на что-нибудь, а он работает всегда с базой, то он тестироваться должен всегда с базой. И то, что мы его не тестируем с базой, это вынужденная мера. В общем-то, вот все эти моки и так далее - это вынужденная мера. Если бы мы могли это этого не делать, мы бы этого не делали, но оно работает медленно с базой, а там надо ресетить state, там надо вот всё это делать. Поэтому мы извращаемся и засовываем туда мойки просто, чтобы оно быстрее и предсказуемее выполнялось. Но при этом мы продолжаем называть это юнит-тестом. Ну, мм, чем больше движущихся частей, тем тем меньше принято называть это дело юнит-тестом, да? То есть если бы как я уже в начале говорил, если бы э слово модуль в названии unit test, которого там, кстати, нету, было бы тогда бы, ну, наверное, вот такие тесты можно было бы называть юнит-тестами, но вот так не принято. О'кей. Давай тебе дальше. Я пока буду немножко набрасывать, а потом мы какой там итог подведём. В любом случае, я думаю, люди, когда нас слушают, и вот понимают, что нету формального определения, которое мы могли бы сейчас назвать, и любой кейс, который мы рассматриваем, чётко бы под него подходил. То есть вот видишь, каждый такой айс. Ну, в общем-то, да, потому что давай теперь э разовьём эту идею. Вот у тебя есть вот эта штука из там трёх-четырёх-пяти классиков, в которых, собственно, QYбиilder там и всё остальное, но внутри, допустим, мы пишем на PHP, внутри у тебя PDO всё равно используется, а оно, в свою очередь, умеет подменять. То есть ты как бы вот эту подмену аля баз получаешь из коробки, если ничего специфичного там не делаешь, да? И, например, да, и в таком случае мы можем, например, достаточно беспроблемно конфигурацию снаружи передавать таким образом, чтобы всё-таки в тестах мы могли использовать SQL in memory. Прекрасно. Опять же, у нас после этого меняется название этой штуки или это также по-прежнему все тесты, которые тестируют эту штуку, являются юнит-тестами? То, что они модульные, это однозначно. Они они они модульные, они тестируют модуль. Наверное, общее определение, как все привыкли называть юнитом, ну, нельзя это назвать юнитом, потому что оно всё-таки ходит в внешнюю систему, которая, ну, не сам этот модуль. То есть вот эта вот подсистема хранения, она не является, ну, мы как бы не не особо должны её даже тестировать. Вот она может повлиять на вот эту вот функциональность, на тесты, при этом фейла в самом коде не будет. То есть вин-тесты, они, как правило, должны проверять логику в коде, а не взаимодействие между кодом и какой-то внешней подсистемой. Ну да, из такого определения мы исходим, мы, точнее, можем логическую дальше цепочку построить, что если, допустим, мы этот ПДО замокали, то фактически как бы тестируется непосредственно вот весь этот модуль, но при этом сами заску не выходит, и мы снаружи проверяем, что а был ли вы. Ну тогда уже больше похоже именно на unюниit. Хорошо. А теперь смотри другой вариант. А мы берём эту же систему и у нас появился программист архитектор, который говорит: "Так, надо сделать чуть больше кода". И эта система разрастается из трёх классов до, допустим, тридцати. Ну, это такой прямо уже такое прямо солё. Он сидит этим занимается. Сложность с точки зрения как бы взаимодействия с базой, допустим, там не выросла. Ну, те же самые в конечном итоге и запросы, там инсерты, селекты. Он скорее абстракцию в коде построил. Хорошо, что в таком случае, то есть модуль-то уже фактически превратился во фреймворк, так-то, по большому счёту. Ну, то есть когда там у тебя уже 30 классов, понимаешь РМ не просто так фреймворками нажимают, они на атектуру довольно сильно влияют. Где здесь та граница, когда мы говорим: "Слушай, ну тестированиние этого модуля уже сложно назвать юнитом. Там у вас такой объём мм всего, ну, допустим, даже если мы изолируем базу данных, да, убираем её, что как бы вот если вы отдельные классики будете тестировать, тогда это будет юнит. А если всё вместе, сорян, нет, это юнит". Даже несмотря на то, что с точки зрения приложения, но это всё-таки как бы модуль один. Ага. Слушай, в любом вообще сложном коде, когда у тебя вот какой-то модуль разрастается настолько, что там внутри свои подсистемы какие-то появляются, по факту эти подсистемы - это тоже модули. Если их дизайнят хорошо, то у них тоже есть интерфейсы и их можно взаимозаменять. И тогда можно, а, тестировать вот эти, скажем так, подмодули, делать там юнит-тесты, а на уровне вот этой вот всей системы оставлять какие-то тесты, которые, ну, уже можно называть интеграционными, потому что они объединяют в себе, а, взаимодействие нескольких составных каких-то вот частей. С другой стороны, если у нас пакет не сильно большой и у него нет вот такого вот публичного IP никакого, ну, то есть не знаю, если мы там возьмём, то это там есть package level, то есть, или там, не знаю, C возьмём, в него есть всякие экспорты, вот хедеры, вот это вот всё, а всё остальное не торчит наружу. И нам, а, ну, возможно, бессмысленно тестировать какие-то отдельные части внутри, если они никем больше не используются, а протестировать просто вот публичное что-то, что наружу уходит. У меня тогда в связи с этим вопрос. Я знаю, что опять же многие люди, кто за Юнита, они прямо за максимальное тестирование всего, в том числе внутреннего интерфейса. Да. Ну как, нет, внутренний интерфейс нельзя. Не, я против, я прямо супер против. То есть смотри, а внутренний интерфейс, ты что имеешь в виду? внутреннее состояние, там приватные всякие методы, вот это вот всё или или не обязательно приватные, но то, что не является публичным апию. То есть понятное дело, что у нас мы не всё там прайватами делаем, да, у тебя там разные классики могут быть. Ну и соответственно PHP, к сожалению, нет такого понятия, как паб методы, которые не являются паб. Ну, мы можем отдефайнить какие-нибудь интерфейсы, но если у нас публичный метод торчит, а, и при этом кем-то используется, и это не через интерфейс, если интерфейс при этом есть у этого класса, то это, ну, на самом деле, это ошибка проектирования. То есть так не должно быть. А если он всё-таки через интерфейс паб, то видимости уровня пакета а у нас нет в PHP, поэтому всё, что паблик, ну, сорян, это публичная IP. Угу. Всё, я теперь понял точку зрения. Ну, тоже бывает, когда у тебя, вот, как мы говорили, абстракция работы с базой данных. У тебя, когда там появляется, ну, реально 30 классов, да, у тебя они по отношению к друг другу, модули внутри могут работать по интерфейсам, по какому-то аля такому публичному. Но это публичная опино исключительно внутри модуля, то есть с точки зрения внешнего наблюдателям PHP технически это не так. Да. Дада. Мы про смысловую про смысловую часть, да, да. И получается, что в данном случае ты считаешь, что, допустим, если мы так спроектировали и у нас действительно вот есть внутренние модули, да, их снаружи не видно, но они внутри используются по сути как паблики внутри этого модуля, то тогда мы их должны протестировать. Ну не должны мы можем их протестировать на тему должны. И тестов это, в принципе, никто никому ничего не должен. Хотите спать спокойно, тестируйте. Не хотите спать спокойно, хотите весело, не тестируйте. Если дёшево, а не тестировать, ну тогда не тестируйте. Угу. А ты в каком бы случае протестировал? Вот когда мы именно про такой кейс говорим? Именно про такой кейс, когда оно настолько настолько сложное, что и настолько долго вызывает много времени занимает ретест, что я этого не хочу больше делать никогда и я хочу плойд в пятницу. Вот, знаешь, я хочу прямо это подчеркнуть, вот с опытом реально ты именно так как-то начинаешь воспринимать, да, у тебя нету там критерии именно формального, чтобы ты подошёл к каждому человеку сказал: "Это делать надо вот так". Ты просто на ощущениях это понимаешь. То есть ты начинаешь это делать и понимаешь, что затраты твои умствен там ментальные, скорость тестов, восприятие системы такое, что лучше эту часть отдельно пойти протыкать, чтобы потом не испытывать. М, ну да. Ну это, кстати, мысль такая не очень простая, потому что часто стараются, особенно когда только учатся, к инструкциям, типа, ребята, а какие правила, какие критерии? И по факту получается, что тестировать, что не тестировать. Ну типа да, здесь критерии интересные. Критерии интересные. То есть, то есть, смотри, по-хорошему бы ну, на самом деле, в коммерческих проектах и тем более в Openрсе надо тестировать типа вообще всё, чтобы прямо вот были гарантии, все дела, всё-всё, чтоб супер офигенно, круто. Но так не получается, потому что, чтобы всё покрыть тестами, как бы большинство покрывается легко, а вот последние там 20% дожимаются очень тяжело, чтобы покрыть реально все вот эти вотсы, там всёвсёвсвсёвсё, каждую строчку кода тяжко, потому что там есть, ну, какие-то обработки ошибок, которые почти никогда не возникают. Ну и ладно. Там и вот эти вот записи влог какие-то, на которые нам, ну, неважно, записался, но или неважно. А чтобы добиться стопроцентного покрытие, нам приходится всё покрывать. Мы это делать не хотим. Есть ещё другие кейсы, когда у нас есть коммерческая система, и мы знаем стоимость, стоимость её падения, стоимость ошибки. Если эта стоимость чрезвычайно низкая, а при этом тестировщики стоят не так уж и мало, то, ну, нафиг. Ну, нафиг в том плане, что напишем тест при любом раскладе на такую часть, которая критиче. Или же бывает, что наоборот тестировщики стоят очень мало, стоимость простоя, никакущая. Тесты писать это время разработчиков. Разработчики стоят немало или же ещё круче. А падение системы вообще в ноль. Ну, упало и упало. Мы её там за час подняли, ничего не случилось, никто не умер. Все довольны, всё хорошо. Допустим, кто-то подождал, наши, допустим, операторы тестировщики, а их вообще, в принципе, нет и не надо. Зачем? Они дорогие. А разработчикам теста писать ещё дорого. очень дорого. Ну, какая-нибудь админка дурацкая там, которая ни на что не влияет. И мы, короче, делаем наших пользователей по факту тестировщиками. Вешаем туда обработку ошибок, чтобы оно там в телегу там или куда-нибудь ещё стучалось, там, мол, всё завалилось, ну-ка идите, поправьте. Вот. И вот так живём. Тестировать в этом случае не надо. Это дорого, это нецелесообразно. Вот мы сейчас это запомним, потому что я как раз за то, что в таких случаях интеграционные тесты практически бесплатны, если мы говорим про современные фреймворки. Ну и тут единственное хочется ещё кое-что перед тем, как мы пойдём вот в такую дебатерскую часть. А у тебя получается как бы одновременно опыт с двух сторон. Просто когда люди читают книжки, обсуждают все эти вопросы, неявно подразумевается, а, точнее так, у каждого человека, который участвует в этом этой дискуссии, у него м восприятие вот я считаю, что делится в первую очередь на две части. Первое - это те, кто пишут прикладной софт, да, и это вообще один мир, и те, кто пишут именно библиотеки, фреймворки и всё остальное. и подходы и восприятие жизни у этих людей кардинально отличается, потому что я пример вот приведу, который часто всплывает в таких местах. Кстати, я в статье даже про это написал, и мне начали предъявлять на тему того, что влияет ли на дизайн м тесты. И я написал, что несмотря на то, что про это часто говорят, а его влияние переоценено. Почему? Потому что, да, я как человек, то есть я как бы как человек, который пишу библиотечеки фреймворки, я знаю, насколько тесты сильно влияют на дизайн, потому что ты как бы таким образом заставляешь себя начинать с использования. Он именно думать тебя правильно заставляет. Но дело в том, что когда ты пишешь в библиотеке, у тебя нету вообще за что зацепиться, если ты всю жизнь не занимаешься этой штукой. Ну то есть, как правило, у тебя интерфейс типа ты такой: "А как вообще сделать?" У тебя возникает очень много вопросов на тему того, что ты Ну, это ты всё-таки про тестф подход. Ну, даже не тестф, а, скажем, ну, да, тестф, кстати, тут имеет значение, ты абсолютно прав. А, но я имею в виду, что всё равно это связано с тем, что там и тесты могут быть пониже уровня, всё такое. А когда мы говорим с тобой про прикладной код, вот где у тебя просто очередная модель, очередной контроллер и так далее, для того там вообще понятие, что типа задизайнить что-то через тесты, оно, как правило, отсутствует. У тебя даже до довольно большого уровня приложений всё сводится к модель с набором методов. максимум вводится слой сервисов, и при этом оно всё довольно грязненькое в проектах. То есть такого, что там кто-то идеально вот этому построил, такого в жизни не бывает. И получается не обязательно, ну, как такового дизайна, то есть настолько мощно фреймворки базовые уже вкладывают вот этот дизайн через ОРМ или, кстати, в каких-нибудь больших фреймворках уже сервисы встроены, плюс какие-то готовые, знаешь, такие распространённые библиотеки, принятые в этом фреймворке, что вообще не думать о дизайне можно до размеров многих лет разработки и заработ каких-то больших денег. То есть вот мой поинт именно в этом. И чаще всего это, в принципе, нормально будет, потому что к этому моменту и фичи поменяются, и проект поменяется, и выкинется всё нахер, и так далее, и так далее. То есть, короче, влияние тестов на дизайн для прикладных разработчиков в фрайворках и готовых экосистемах невероятно меньше, чем людей, которые разрабатывают библиотеки. Да, тут согласен, но оно далеко не нулевое. Вот это тот самый момент, который ты упомянул, что фреймворк, а задаёт тебе, ну, какой-то, скажем так, архитекту архитектурный стиль, каркас, который, на самом деле, никакой архитектурой не является, но на котором можно какое-то время ехать и достаточно продолжительное время. Вот. И если этот каркас тебе не мешает писать тесты и, возможно, чутьчуть помогает писать тесты, если в этом каркасе даны, ну, не знаю, примеры сервисов, у которых там зависимости явно указаны, ещё что-нибудь такое, то, в принципе, тесты не влияют. Если же у тебя везде active recкоord, и ты этот active recкоord не додумался загородить каким-то слоем абстракции, чтобы, ну, там в юниты ещё не просочился. Вот. И в этом случае у тебя достаточно жёсткая завязка там на базу идёт и так далее. И вот тут попытка протестировать немножко столкнёт тебя с реальностью. что с твоей архитектурой что-то не так, и твои тесты повлияют на твою архитектуру. О, вот, вот тут мы с тобой прямо поспорим, потому что, а, у меня другое восприятие этого добра. Единственное только, знаешь, сначала с чего начну, когда ты говоришь про то, что, ну, то, что они дают, это не архитектура, а я не соглашусь по одной простой причине. Мы скорее просто к этому настолько привыкли, что когда, знаешь, такой вот некий базисный уровень тебе дают, ты в какой-то момент перестаёшь воспринимать это как некое чудо. Ну, у нас там, знаешь, мы там есть такой шоу, где там чуваки обсуждали, что ты летишь в самолёте, у тебя работает интернет, и ты ещё такой недовольный, что он у тебя там, значит, достаточно хорошо Дадада. Типа ты вообще ты понимаешь, что происходит. И вот, например, то же самое. Мы же с тобой как пичпишники-то знаем, что такое писать, когда нет фреймворков. Это вот в других языках такого не было. Они сразу все начали с фреймворков, и они не видели. Но сама идея того, что у тебя типа в отделён там от обработки и вообще база как-то вынесена, это вообще-то фантастическая штука, которая изменила очень сильно разработку и вот это как раз дала вот этот минимальный уровень, когда стало хорошо. И самое смешное, а по поводу Некста, ты, наверное, знаешь, да, что когда буквально пару лет назад показали этот в Несте вот эту, что ты можешь в GCX делать запросы в SQL, и люди такие типа: "Чуваки, мы всю жизнь от этого уходили, вы с ума что ли сошли? И потом кто-то, буквально на днях видел прикол, кто-то сказал, что типа следующую версию NextJS вмерт в PHP, потому что и мы и мы как бы закроемся. Короче, я я считаю, что всё-таки он дал очень многое. Просто многие не видели, как бывает по-другому, и считают, что это некий базис. А как ещё? Как знаешь, вот мы же не удивляемся. Смотри, это как бы общая архитектура, а, ну там разделить слои, там во в'ю несовать. Это такие вот прямо best practices, которые применимы вообще к любому, в принципе, проекту. Я бы не назвал это архитектурой проекта. Я как бы это, ну, не знаю, архитектура гигиена. Вот тут бо больше ничего нет, да. Это не не специфичные для приложения архитектура. Она не разделяет, а как ты как раз вот выделяешь вот эти вот модули внутри своего приложения. Что ты группируешь, что ты уносишь рядышком, куда ты складываешь, да, где у тебя получается много правок получится и они бесполезные, чтобы что-то там менять или не получится. Вот это уже зависит от архитектуры, которую ты сам придумал. Я бы даже циферку назвал. Я думаю, что проекты, ну и очень грубо, до 100.000 строк кода, а это вполне себе приличные проекты, особенно компактных языках, да, то есть у тебя там уже много логики всякой может быть. Они вполне себе уживаются в рамках этой штуки. Понятно, что там, как правило, во фреймворках есть ещё какие-нибудь вещи, ну, типа авторизации, там, знаешь, вот ещё какой-нибулезные джобы асинхронные, и в сумме это всё вместе остаётся на поддерживаем уровне. Вот дальше, да. А поэтому я знаю, что придут люди, которые скажут: "А как же ДД, мы вот тут сразу, значит, у нас там вот эта вся фигня". Ну да, можно и так, но это немножко другое. Это уже сами ребята с ходу начинают себе там наворачивать и прикручивать, да, делают себе сами проблемы на старте, которые надо делать позже. Ну, кстати, даже при этом, даже если мы идём про Да, да, идём в какие-то такие вещи, там же ещё есть уже общепринятые м системы, например, события, да, то есть когда у тебя есть разные шины, работа через них. Я имею в виду, что вот когда ты говоришь про MVC, всё-таки там чуть выше ещё уровень. Просто оно не всегда, оно уже, наверное, не настолько широко известное. Но даже если ты возьмёшь тот же Laraвеelль, когда у тебя надо, когда у тебя есть система событий, она встроена внутрь системы, даже если ты начинаешь пользоваться, у тебя уже в 100 раз лучше становится, чем просто при обычном MVC. Хорошо. Я думаю, хуже становится, если честно. Да, это ты имеешь что от бездумного использования или тебе сам дада. Ну как бы концепт на на событиях мне очень не нравятся проекты. Событие классная штука, когда тебе надо что-то прям разделить, но оно на самом деле не разделяет. То есть в коде у тебя явной завязки нет, но логическая завязка сохраняется. А так как в коде её нет, то рефакторить сложнее а смотреть, что куда полетело, что это кто подхватил, кто обрабатывает, выстроит последовательность действий становище очень сложно. Это получается вот ещё один ещё один тип зависимости, который, кстати, вот акл Боб, там Фаулер и все остальные, они его не выделяли. И недавно вышла офигенная книга, называется Balancing Clin in Software Design, автор Влад Хонов. И вот он, короче, добавил ещё несколько типов вот этих вот связей, которые неявные. Они самые злые, то есть они на на когнитивную сложность влияют очень плохо. Если это, конечно, немножко не тема нашего подкаста, но в любом случае, да, если ты начинаешь просто на событиях разделять вот эту логику на этапы, это, конечно, в ад превращается. Мы, например, там чётко это ограничиваем, ну, например, аналитикой, то есть когда тебе нужно действительно Ну да, да. Нене не, если если ты делаешь какой-то универсальный модуль и этот универсальный модуль, не знаю, там отправляет э ну что-то закончил и вот выкидывает события, а кто-то там в твоём приложении подписывается, у него отправляет письмо. Отлично, вообще. Почему нет? Да. Да. Да. О'кей. Всё хорошо. Давай теперь, э, возьмём прикладное приложение, да, и поговорим в рамках него. Я знаю, что всё-таки тебе тянет вот имя библиотечную историю, но я исхожу из того, что сейчас сходу, наверное, наша аудитория в первую очередь это люди, которые пишут прикладной код и в рамках него рассуждают. А по а книги в большинстве своём, вот если почитать книги, они все тяготеют к тому, что как будто мы, знаешь, проектируем, не знаю, свой собственный фреймворк. Поэтому я бы хотел немножко приземлить вот такой к реальности, которая более простая, более прагматичная и где меньше всяких таких заморочек. И в рамках этого поговорим. Вот, допустим, да, мы работаем в классическом фреймворке, у нас есть какая-то ормка, у нас есть то, у нас есть сё, у нас пятая, десятая. А поскольку здесь как такового модулей, который вот я прямо такой сел и такой спроектировал некий прямо под систему, оно, конечно же, бывает. Но, честно говоря, по отношению ко всему общему коду, но это всё-таки не самая частая задача. Либо как минимум вы уже доросли до какого-то объёма, где у вас, ну, уже, во-первых, люди есть отдельные, да, которые чем-то подобным занимаются. Даже если не фултайм, но всё-таки чем-то занимаются. А так в основном, что у вас там контроллеры, а, апишка, модельки, ДТошки, ну, и погнали, да, вот вокруг этого мы там пляшем и прыгаем. И мой поинт, что в таком случае, в принципе, писать что-то, кроме интеграционных тестов - это какая-то шляпа, потому что интеграционный тест в таком случае, мы говорим про встроенные системы, когда мы берём вот Richфреймвор они называются, да, это классика жанра, это Janga, Rails, Lara, Symphony, Phenix, например, там, ну, и другие какие-то, да, не микрофреймворки, а то вот какой-нибудь Экспреessс или гошные фреймворки, они в этом плане мало что дают. Вы там охренеете всю эту систему самим создавать. Кстати, поскольку многие работали только с микрофареворками, ну как когда ты создашь, они они будут очень похожи, на самом деле. Вот то вот. Да. Но часто те, кто это делают, они я у меня вот как раз с гошниками была переписка, они как раз, когда узнали вот про, знаешь, я там что откатываемые транзакции и так далее, они такие: "Чёрт бери, а мы тут, значит, ну страдали и вообще всем этим заморачивались. Оказывается, это есть". Вот. А у нас этого нет. И поэтому мы это всё переизобретали, потому что мы другого не видели. Так вот. И в этом случае мой поинт такой, э, если вот особенно мы в рамках только баз данных работаем, особенно, да, потому что понятно, что там потом шины всякие могут быть, то у тебя как выглядит типичный такой тест? У тебя он выглядит так. А, подготовка данных небольшая. А это отдельный немножко вопрос, потому что там типа базу какую надо какие-то данные сунуть. Для этого там разные механизмы используются. Фактически однаединственная строчка - это вызов какого-то урла внутрь. Причём тут хочу подчеркнуть, что опять же, когда это всё интегрировано внутри фреймворка, он чаще всего HTTP даже не поднимает. То есть у тебя оно выглядит как типа там get или пост запрос, но мы знаем, что на самом деле он просто внутрь проваливается сразу объекта приложения, да, и получается это быстро. То есть нет httpка, нет портов, нет ничего, он просто внутрь попадает. Там потери в первую очередь, ну, насчёт быстро я бы поспорил, но они потери всё-таки там идут в первую очередь, сам знаешь, на данных на запросах ба на на самой базе. Угу. Да. Да. Потому что если это убрать, там скорость всё равно довольно приличка. Причём и вот тут тоже, кстати, возникают вопросы, а проверка чего? Вот я сторонник того, что уходить прямо в в end to в таких тестах, ну, не надо пытаться проверять, какой там внешний вид. Или опять же, э, опишка в опишке я за то, что проверка вообще должна идти не на уровне таких тестов, это всякие API спеки и соответственно автоматические есть автоматический инструментарий, который позволяет во многих фреймворках это вообще без тестов чекать, что у вас ответ соответствует там формату. Вот. Ну, это в идеале, то есть не везде такой есть, но стифай, ну, практически везде, то есть по по Open API по всё умеет практически проверять. Ну, если глубокая интеграция, да, то тебе даже таких тестов, то есть тесты на логику, да, например, там ты пост делаешь какой-нибудь апдейт, ещё что-то, но вот тест, что у тебя прямо там структура вернулась правильно и ты проверил её там JSON схемой, например. А если кто-то так делает, ребят, ну это рукопашка, то есть современная интеграция фреймворки позволяют такие тесты не писать и не думать об этом, особенно если у вас ещё и статика там прикручена. Так вот, а и по большому счёту, поэтому мой поинт в том, что проверять по сути надо прямо модельки. То есть ты смотришь, например, что там, не знаю, юзер был активирован или ещё что-то в таком духе произошло. Модельки, да. Да, ты прямо просто пишешь там. Базу на фоне, естественно, речь идёт про базу, но когда мы работаем с actтив рекордом, а это большинство таких фреймворков, то мы просто извлекаем модельки и смотрим, что там произошло, изменение состояния, которое мы, собственно, ожидали. А здесь я [ __ ] как против. Вот давай спорить, потому что это нельзя, нельзя в тестах, которые вот, ну, типа или вот что-то в этом роде интеграционные, а, которые дёргают там AP внешне и так далее, использовать внутряк. Это то же самое, как в юнитах залазить в приватные методы. То есть твои рекорды, твои модельки м они меняются и у них могут отваливаться методы и так далее, а твой тест твой развалится, получается. Ну и на самом деле и структура базы твоей тоже может меняться, и он тоже может развалиться. То есть это как бы, ну, в моём понимании это немножко вынужденная мера. То есть у себя, если я тестирую на уровне именно, ну, допустим, опишку какую-то на уровне вот браузера, как бы браузера, там, URL, в PHP, естественно, тоже есть вот эта вот штука PSR седьмо request response. Мы создаём объект реквеста, кидаем вот во всю эту кофеболку. Она там что-то молитмолит, выплёвывает нам респонс. Мы начинаем этот респонс проверять. И я немножко не согласен, что опять же это должен быть один вызов. То есть мы можем что-то там постом закинуть в вот в эту вот систему, получить ответ, потом геттом запросить дополнительные данные и проверить, что данные надались правильные. Лезть для этого в базу. Ну а зачем? То есть мы можем внутреннюю структуру хранения 100 раз поменять. Тест у нас от этого развалится. Я так не хочу. Я хочу, чтобы у меня тесты оставались зелёными, а я себе сидел и рефакторил. Отлично. Отлично. Смотри, значит, у меня здесь два поинта. Первый, в идеале, вот то, что ты говоришь - это такая каноническая ситуация, которая описана в книжках, что ты вот делаешь запрос и потом смотришь. А я к этому отношусь следующим образом. Чаще всего кост этой штуки, он сильно выше, чем посмотреть модельки. Почему? Потому что чем сложнее у тебя система, тем больше разнообразия. Потому что одно дело у тебя просто крут. Другое дело, когда у тебя какая-то фигня может запускать цепочки изменений, которые, например, влияют на твой биллинг, на твои доступы, например, на какие-то там вот в нашем случае там курсы, которым ты доступ получаешь и так далее. И получается, для того, чтобы это проверить, у тебя может быть большая цепочка совершенно разных разнообразных внешних вызовов. И мне гораздо проще это проверить на внутреннем API. Ну, потому что модели - это всё-таки довольно открытая штука, если ты в рамках Activeв Рекоordда работаешь. Поинт про то, что там развалится, объясню. Тут ещё сильно зависит, тут я считаю, что восприятие ещё сильно зависит от системы типов и что ты пожаешь по словомрефакторинг. То есть, грубо говоря, если, например, у меня переименовывается метод, нет такого, что он переименовался в модели и у меня упали тесты. Это значит, что если он переименовался, он переименовался по всему проекту, потому что это просто стандартный рефакторинг. То есть даже до тестов не дойдёт, у меня это будет исправлено. Причём исправлено не потому, что я пошёл менять эти тесты, а потому, что если у меня название такое происходит, кстати, крайне редко. То есть, если, например, у юзера есть конечный автомат, связанный с его процессом регистрации, вот как, например, я там и создал его в 2013 году на Хексте, так он такой же и остался за там сколько лет, да? Потому что у вас есть ожидание имейла, у вас есть активный, у вас есть забаненный и так далее. Соответственно, там юзер типа активный, оно как было, так и осталось. То есть этот тест, наверное, вот там больше 10 лет не менялся. Я хочу показать, собственно, этот тест. Да, наверное. Угу. О'кей. Ну, чтобы сразу как бы наглядно посмотреть. Слушай, мне мне на самом деле знаешь, что вспомнилось? Мне вспомнились миграции. То есть была такая практика, что в миграциях использовали тоже вот модельки этого самого Активрекорда. И вот это прямо вот дико неправильно. Вот, кстати, у меня сейчас переключилось. Ты, наверное, тоже всё видишь, да? Ну, это опять относится скорее к опыту, когда ты с этим сталкиваешься. Это действительно правда, если Тут есть несколько условий. Я знаю, про что ты говоришь. Ты скорее всего говоришь про то, что если ты начинаешь так делать, у тебя в какой-то момент, когда моделька меняется, у тебя проходится состояние базы и модельки. Угу. Я, может, там три пункта назову, давай. А в целом я с тобой согласен. И поэтому, например, буду приводить примеры на рельсе. Просто потому что там как раз эта система, я считаю, сделана очень классно и мне нравится, в общем-то, то, как оно работает. Я вижу, ну, в общем, сравнивая с другими фреймворками. Если мы говорим про миграцию именно ддэльки, то есть структуры баз данных, там просто есть некий свой язык, который А что далеко ходить? Давай просто и посмотрим. У нас сегодня будут примеры. Сегодня, видимо, я начну с примеров, да? А потом, соответственно, ты давай вот я тебе прямо с сразу покажу DB migrate. Угу. Вот так. Давай я лучше. Где структура? Нет, я хочу такой, где change есть. Сейчас я тебе покажу, чтобы было видно. Вот. Вот прекрасный совершенно пример, да, некий свой дслька, который описывает создание базы. В чём, значит, удобство? Во-первых, это не модельки, да? То есть тут это то же самое, что было бы SQL написать, но чем, например, это лучше, чем SQL, а обрати внимание название метода, он change называется. Я вот не знаю, сколько ты с этим знаком или нет, потому что в основном это который и up, и дау одновременно, да? А этот как бы авто, да, автооткатываемый метод. В этом плане как бы его удобства при этом, а когда речь идёт про изменение именно данных, тут мы действуем, знаешь каким образом? Если у тебя производительность важна, там часто селект может очень крутой, да, быстро написать и тем более ишка сгенерит, вообще не проблема, но мы знаем, что мы можем использовать модели. Опять же, у нас компактная команда, понятные проекты, мы понимаем, что мы делаем. Знаешь, почему? Потому что, когда у тебя в системе миграции встроен вот этот вот дамп схемы постоянной, да, и ты можешь миграцию в любую секунду удалять, то для нас это особо не является проблемой, потому что мы, как правило, э как только там накапливается хотя бы 10 миграций, мы предыдущей миграции просто удаляем. А зачем? Ну я как бы вот дам дам схемы. Я видел такую штуку. А зачем это нужно? А зачем они тебе физически нужны, если они даже просто банально использоваться не будут? Смотри, как устроено. Вот видишь, вот это дампсмы, как работает вот эта система миграции в рельсе. И, кстати, в Ларавеле это тоже сделали. Сейчас их долго просили симфоне есть, это дамсмы и вшке, по-моему, тоже был дамсхемы, но я как бы не пользуюсь особо. То есть я накатываю все миграции и нормально. Вот объясню проблему у тебя. Ну представь, проекту, допустим, 10 лет. Он я бы думаю, что у нас несколько десятков тысяч миграций было бы, потому что у нас иногда месяцы бывают, когда их сотнями накапливается. Во-первых, тупо, ну, во-первых, это долго. Во-вторых, в принципе, зачем вот это всё предыдущее? И схема работает. Ну, то есть в современных проектах оно работает так. У тебя накатка миграции - это два, на самом деле, две операции. Первое - это загрузка схемы, а внутри схемы, вот обрати внимание, в схеме данных нет, кроме миграций. То есть миграция - это единственные данные, которые он сюда прямо запихивает. Ну да. А смотри, как он их запихивает. Он их запихивает не миграциями, он запихивает сюда их понятием версия. Что это такое? Это, собственно, последняя, последняя миграция, которая была применена. И, соответственно, у тебя работает это так. Ты делаешь dB схема, там такая таска есть, он её загружает. И после этого, как только ты просто миграции выполняешь, он выполняет миграции только старше вот этой вот версии. Это поэтому у нас удаление миграции - это не в смысле какой-то дополнительный там это надо, не надо делать, они просто не нужны. И поэтому ситуация, которую ты описываешь, понимаешь, да, её как бы вероятность возникновения и проблематичность, она стремится к нулю. Да, она стремится к нулю, даже если мы не удаляем этими миграции. Ну, их можно не удалять, они просто лежат и лежат. А в этом смысле, ну они применяться не будут. Они что-то их удалишь, что-то их не удалишь, они будут одинаково лежать и лежать. Я считаю, знаешь чего? Ты модельку можешь удалить. Вот тогда у нас были, кстати, такие приколы, когда, ну, просто долго не удаляли миграции, модельку удалили и там бам, что-то грохнулось. Ну, типа из автозагрузки банально. И вы в миграциях использовали модельку? Да-да, я же тебе говорю. Да, в таком случае это вообще вот для меня это табу. То есть нельзя так делать как раз по этой причине. А я уже вот как раз, собственно, и рассказываю о том, что мы используем мы можем не использовать модель по причине производительности, но по причине той, которую ты описываешь, у нас нет такой проблемы именно из ��ого, что миграции удаляются. Не потому, что это типа дополнительная работа, а просто зачем нам нужна папочка с тысячами файлов? Это тупо мешает, а автоматика сама всё прогружает. О'кей. Вот. А кому как удобней, да? При этом, не знаю. Я я просто в истории миграции, вот в этом вот гигантском списке, я частенько поиском по проекту находил, а почему мы что-нибудь грохнули там год назад. А, ну для этого гит есть. Можно, можно искать погиту, но, как правило, вот когда ты по проекту что-нибудь ищешь, оно проще, когда он у тебя лежит. Ну вот я тебе могу пример привести. Вот смотри, а вот приёт заполнения. Это какие-то там штуки сделали. Вот видишь, where update all. Ну да, вот она уже больше не используется, не будет использоваться, естественно. Мы это просто удалим. Немножко отвлечение, но оно вот полезное для понимания. Так, ну что, поехали в тесты. Я как раз хотел тебе, собственно, показать, почему мы используем эту штуку. А я единственно перед тем, как нарнуть тест, одну очень важную деталь скажу, которая мне нравится. То есть вообще моё отношение к тестам в целом. Я не сторонник вообще называть что-то юнитами, модуляными тестами, интеграционными и так далее. А при то есть у меня, как и у любого человека, есть тоже пример такой же понимание, как у тебя, что вот есть уровень интеграции, есть уровень end to end, ну, очевидный уровень там разница, когда ты делаешь to и есть уровень там модуля. А, и слова юнита даже вот как бы в этом нет, потому что, ну, опять же, там микроскопичность функции вообще не важна, потому что у тебя функция может быть на две строчки, три, но при этом она сходила в базу, отправила HTTP запрос и записала файл. При этом не пользуясь никакими модулями, а просто используя нативные функции, да? То есть ты всегда можешь придумать такой кейс, при котором люди будут такие: "Блин, ну чёрт знает, вроде и юнит, а вроде и не юнит". А кто-то скажет: "Да нет, тут строгие правила и всё равно мы найдём схему, при которой это всё развалится". Да? И плюс во всех фреймворках свои ещё понятия. Поэтому я с топлю скорее за то, что вот как у вас во фреймворке принято. Ну, например, мы говорим: "Это тест контроллера". Угу. Мне без разницы, назовёте вы его юнитом или интеграционным или ещё как-то. Я знаю, как они пишутся, я знаю границы, я знаю, почему они так пишутся, где они находятся, их цену, то есть вот такие вот критерии. И меня это полностью устраивает. При этом внутри контроллере может быть просто, не знаю, что-то отдали, может быть, сходили в базу. может быть ещё 500.000 действий, включая там изменения половины баз данных. Это вообще ни на что не влияет. Как был тест-контроллер, так остаётся. Это вот моё личное отношение ко всем тестам. Поэтому мы, например, у себя внутри вообще не используем панетийный аппарат, юнит интеграционный или там какие-то. Мы можем сказать системные тесты- это то, что касается, когда через браузер идёшь, грубо говоря. Интеграционно - это когда на уровне по сути там запросов без HTTP. И всё остальное ниже. Это уже просто это тест модельки, это тест хелпера, это тест там, не знаю, что-то ещё. Ну вот те первые две штуки, которые ты выделил, они, по-моему, важно эти м слои выделять, потому что один из этих слоёв - это тестирование, что твой публичная i, которую ты дал обещание своим конечным пользователям, всё ещё работает, как работало. И даже если у тебя внутри там, не знаю, сломались, ну, то, что называют юнит-тестами, то, что называешь просто вот массивом вот этих всех тестов, это ещё не значит, что для конечного пользователя всё пропало. Вот если поломался самый верхний уровень, то, скорее всего, твои пользователи что-то уже увидели. Это плохо. А, да, но тут уже возникает вопрос коста, да? То есть косты в таком случае огромные писать, когда у вас, особенно микроскопическая команда, там какие-нибудь nэн-тесты - это, честно говоря, удовольствие. Во-первых, это я не знаю людей, которые любят это, если человек не тестировщик, а во-вторых, это это медленно, это ненадёжно, это, не знаю, мне лично очень сложно, поэтому я такие тесты пишу только вот вот как знаешь, вот мы с тобой смотрели, а там чувак, по-моему, на логин, да, написал. Ну вот типа, например, критический логин, регистрация и форматыгу. Вот, вот три таких теста энтуэнтами покрыли. Да, нормально, да? Ну, если что-то другое сломается, ну, наверное, это расскажут. Вот, собственно, пример, то, к чему мы шли. Я как раз хотел сказать о чём. Значит, первое, мм, мой поинт в том, что даже если у вас прикладное приложение, где вы там пишете админку, писать такие тесты очень дёшево и легко, интеграционные, которые, как правило, называются интеграционными. Ну, по крайней мере, в большинстве фреймворков. Вот я их, собственно, показываю. Это админка, тест на апдейт. Здесь создаётся пост, э, это берётся там из фикстур, которые в базу автоматом добавляют. Каждый тест, транзакция там начал, откатил, поэтому мы про стейт не думаем. Здесь выполняется запрос по урлу. Причём, кстати, что очень важно, смотри, это тоже такой момент. Урл не напрямую, а через хелпер, который генерится, поэтому у тебя всегда это у всех нормальных роутеров так и сделано. Угу. Да. Это, как правило, в микрофреймворкав этого нет. Они там прям прям интерполяция этилы даже котенации херачит. А мы просто проверяем, что был редирект, и я, смотри, что делаю. Поскольку блокпост как сущность была извлечена, тут её надо зарелоудить, чтобы она данные из базы забрала и просто, да, и после этого я просто смотрю, что слаг стал му. Ну, понятно, что это тебе ещё везёт, что вот этот вот actтивре в рельсах очень легковесный. То есть, если взять, допустим, пичпишку, то там есть набор всяких разных абстракций для работы с базой. А есть легковесный, типа, ну вот яишный, вот этот вот DB, то есть этобиildдеры прямо вот слой работы с базой, как как ну вот просто слой. Есть яичный acтивре, который ну похож на то, что в рельсах и есть штуки посложнее. Есть доктрина, в ней есть state, в ней есть титименеджер, в ней есть дофига кшей. И вот вот такой вот релод сделать, это себе сразу выстрел в ногу. Так делать не получится просто, потому что оно где-нибудь дозастрянет, и вы получите не те данные, что-нибудь закширование, а на самом деле в базу он не запишется. Там идентити мапы, да. А, кстати, куча куча всего сложного. Я очень хорошо знаю кишки доктрины, потому что у меня был кейс, я потратил на это довольно приличное время, когда я пытался написать свой датамапер в рельсе. Я вот дог называл, кстати, у меня такое название прикольное. А да, и можно найти исходники, и она даже работает. То есть я дошёл до какого-то, так вот, чтобы её написать, я изучил просто доктрину всю наизусть. То есть я просто все гидра, все механизмы, я всё просто по полной программе, да. И поэтому там даже вот этих механизмов дёти чека там несколько, да, как в хибернейте, потому что это кошмар как сложно, да. Ну доктрина она по хибернейту писалась вообще на по мотивам. Хибернейта ещё сложнее, да, ещё ещё круче. Я я их не люблю ужасно. Я, к сожалению, тоже знаю внутренность для доктрины очень хорошо, потому что у меня было несколько коммерческих проектов с этой штукой. И сейчас тоже, кстати, один из них. Вот. И иногда там начинаешь на себя волосы просто рвать, потому что очень простая штука, типа запиши значение в базу и после запиши второе значение в другую таблицу. Вот это становится очень страшным делом, да. А ещё зависимости, когда есть. Вообще у меня в этом отношении, знаешь, если посмотреть мои ранние видео, поменялось отношение. То есть я когда-то прямо хейтил acтиврекер. Такой больше немножко, знаешь, у меня был такой инженерные такие, знаешь, представления о том, что вот смотри, дата мапер - это концептуально правильно, Active Recр - это антипаттерн, у тебя, значит, разделение тра-та-т-т. Потом это, кстати, довольно быстро в своей жизни я прочитал у Фаулера, где он как раз про Unit of work, про identity map, я впечатлялся этими штуками, вот начал всё это делать. И потом в какой-то момент с течение времени я долго не понимал, почему люди хейтят Хиберней, например, да? И потом стало понятно, что вот вот эта вся красота, которая теоретическая очень, за неё многие умные мужи топят, на практике работает довольно херово. И вот это вот тупое, у тебя есть табличка, ты просто в неё сходил, блин, при всех своих ограничениях, недостатках. Там ещё, помнишь, всегда концепция была такая, что типа у тебя вот эта модель может вообще быть в другом месте, и мы не некий делаем унифицированный слой, что там на фоне у тебя в зависимость от ситуации идёт туда-туда. Как показывает практика инженерии вот за там многие десятки лет, это известно не только по этой штуке, это по ирлангу, например, известно, когда у тебя, помнишь, там история такая есть, могут быть на разных нодах процессы, то есть у тебя, грубо они говоря, могут быть на одной ноде, а могут быть на разных, и там тисяшка от тебя прячется, то есть ты делаешь типа посыл сообщения, он идёт на самом деле в другую ноду. И была идея о том, что смотрите, как классно мы работаем, как в неком едином пространстве, а вот это вот распределение, оно прозрачное, это некий нижний слой. Но жизнь-то показала, что у тебя TCP там, а это задержки. И, соответственно, как только начинаются тормоза, у тебя вся эта система стоёт колом, у тебя там бутылочные горлышки возникают. И идея, попытка спрятать как бы нативную вот эту вот историю, что у тебя есть HTTP, у тебя есть файловая система, ещё что-то, оно не работает. И здесь получилось ровно то же самое. Плюс-минус, плюс-минус. Мы на самом деле вот в Е2 у нас есть прикольная штука. То есть у нас можно вот на в этом самом рекорде сделать relation на модельку, которая лежит в совершенно в другой базе. То есть одна в, допустим, в постгрейку лежит, а вторая в редисе, и она будет работать. Ну опять же с учётом того, что ты должен про это понимать, потому что у тебя может быть весёло. Нет. Нет, нет. Ну, ну как нет? Транзакционность оно работает, да? Транзакционность работать не будет, конечно же. Я именно это имею в виду. То есть ты как бы вот эти вот все поги абстрагировать эти штуки, они всегда упираются в то, что у тебя сеть, исключения, транзакции, атомарность, там, что хочешь. Да, оно работает, но но есть нюансы. Есть нюансы, да. Да. Вот поэтому я говорю, в Орланге то же самое. Оно всё классно работает, пока у тебя сеть идеальная. Как только она не идеальная, ты тут же получаешь весёлые истории. И более того, там в итоге оказалось, что надо писать свой собственный свою собственную систему, чтобы это всё работало. А, наверное, чтобы, знаешь, вот больше не занимать этим эфир. То есть я просто подведу итог, что вот вот это так выглядит тесты, которые я пишу. А опять же, почему я не делаю тут же второй запрос и не смотрю где-то ещё по двум причинам. Первое, а, как правило, изменений происходит много в разных местах, и тогда вы обалдеете, все эти выборки делать. Более того, они не все есть, не всё является частью внешнего контракта. То есть, например, многие состояния это просто как бы часть базы. Второе, ну, понятно, что это производительность ещё будет сильно влиять, когда у вас там вот этих вот запросов и сложнее тесты поддерживать. В-третьих, как я тебе уже говорил, как когда говорят: "Ну вот у тебя упадёт там 100 тестов, если что-то поменяется". Если у вас нормальный редактор с поддержкой нормального рефакторинга, вы банально даже не заметите, если у вас переименовывается метод, он тупо переименовывается сразу везде. Вот поэтому как бы проблемы-то в принципе такой не существует. Ну, а навигация просто банально, там всё проще, потому что, если в двух словах сводить как бы всю историю веба, а я это, наверное, так воспринимаю, что по большому счёту всё, что мы там не делаем, в конечном итоге это прослойка, которая просто более удобно позволяет нам привести в базу в определённое состояние. То есть я не являюсь человеком, который относится к базе как я знаю, что многие так не считают, но и более того, я тоже разные этапы в своей жизни проходил о том, что база - это типа некая [ __ ] которую надо скрыть за слоем там, абстракций. Я считаю, что база - это ядро, это корень, это вообще всё. И это, знаешь, такое ещё ментальное упражнение, которое позволяет мне не овенженирить, о том, что в конечном итоге задача в том, чтобы в базе всё оказалось в правильных стейтах, в правильных местах. Понятное дело, что мы не напрямую в неё ходим, вот через модельки, но вот такое вот у меня кредо и на на тех проектах, на которых я работаю, на том объёме кода, на том объёме команд, это работает хорошо. То есть вот за столько лет там практики своей именно в этих кейсах я не упёрся. При этом, если бы мы с тобой сейчас говорили про разработку фреймворков и моделей, я бы уже, конечно, другие слова говорил, потому что, естественно, там совершенно другой подход, и я тоже, в том числе, участвую в разработке. Слушай, не только фреймворки, на самом деле, если мы возьмём ещё и чуть другие вещи, а именно проекты, где у тебя мало стейта и много обработки, то всё будет не так. Ну, в нашем случае обычно это просто доптесты. То есть, грубо говоря, у нас есть кейс вот на хекстате очень много с практики редактор вот это вот, знаешь, у тебя там по сути свой Хекс. Хекс - это та штука, которая вся про базу. Она вся абсолютно вся про состояние базы, не считая практик. То есть вот практики - это всё-таки там инфраструктурщины очень много. И там действительно, и да, инфра там вот это вот всё это понятно. Хорошо. А есть проекты, которые про дофига всякой разной обработки, которые аа что-то там дико считают, которые а что-то там делают, которые генерят тебя не там опишки респонса, там, не знаю, какой-нибуд бинарный файл или ещё что-нибудь такое странное делают, которые обычно не делают. Обычно мы действительно берём и перекладываем значения, чуть их там меняем, там круды какие-то делаем, ещё что-то такое. Ну, бизнесу это как бы обычно и надо, но бывает по-другому. И вот когда по-другому, то и это и влияет и на то, какие у нас тесты и какая у нас архитектура, и, в принципе, как нам надо думать про это всё. Вот. А здесь мы, кстати, видим чуть другой подход. А, смотри, это миграции в Ишке. Они изначально, кстати, были рельсами навеяны. И здесь у нас есть, ну, colon builder, типа, и мы сделаем вот create table и вот такие вот вещи. И как бы модельки мы не используем, то есть мы работаем с точки зрения ддэльки, да, ну, ровно то же самое, что я и показывал, то есть такой же язык, да, да, примерно так же. О'кей. А если мы посмотрим на мм ну не знаю, тесты, которые, аэ, на твои похожи, то и это, наверное, вот если взять там простые там какие-то сайты, вот сайт, допустим, фреймворке. И эта штука написана на фреймворке codtion. Codception имеет у нас классические юдиты. Тут у нас ни фига нет, да, но в других местах есть. И да, есть вот эти вот функциональные тесты, которые, ну, просто вот пойди на страницу, посмотри, и здесь оно, ну, вот прямо так и выглядит. То есть я на странице вот такой-то здесь это, конечно же, опять же вот роутер, как ты и говорил. И мы смотрим, что вот открыли, потом посмотрели, что там есть какая-то фигня написанная, заполнили форму, засабмитили, вот, и посмотрели, что оно там что-то скюмом нам сказало, и всё. То есть это вот такие вот приёмочные тесты в базу. Мы здесь опять же пытаемся не лезть. Я лично против того, чтобы в базу в этом случае лазить, если, конечно, у нас есть такая возможность. То есть я не пишу тесты как бы догматично. Если нужно лазить в базу, то, ну, придётся, да. Ага. Я просто, знаешь, хотел единственно только уточнить, чтобы у людей не возникло ощущение, что это реально одно и то же. Вот то, что ты сейчас показываешь, это, конечно, для меня это другой уровень. Это уровень уже не интеграционных тестов, это end toend тест или мы их там систеend. Чтобы люди не путались, да, ребят, вот эти тесты, они прямо браузер открывают. Это очень тяжёлые тесты со всеми. Слушай, не обязательно. Ну, она сильно тяжелее, чем просто запрос в код сделать конкретно конкретно вот здесь оно действительно открывает у нас браузер, но оно может и не открывать браузер, потому что вот все вот эти вот дела, они могут абстрагироваться, и все вот эти вот штуки, они могут всего лишь формировать у нас объект реквеста. Нет, объект реквеста. Потом прогонять через ядро нашего фреймворка, который выплёвывает объект респонса, а сам ещp запрос не делать. То есть это возможно при сохранении практически всего вот этого вот синтаксиса. Кстати, интересно, в таком случае, наверное, дебак, ну, output нужно классным делать, иначе ты там охренеешь, да, если он не нашёл какую-то строчку, а у тебя по сути там htмэлька, как он её покажет тебе? Слушай, да не, ну htмэльку он покажет, он просто вывалит гигантскую htмэльку. И все вот эти вот тесты, чем чем у тебя выше уровень тестов, если это там апии тесты, если это тесты какого-то HTМ, это показывает тебе, что-то сломалось. Что-то сломалось примерно вон там. Оно не покажет тебе, что сломалось, почему сломалось, где сломалось. То есть сам ищи. Э-э, дальше тебе либо поможет тест, который ниже на уровень, либо, если у тебя нет его, ну, сам сиди, разбирайся. Это вот минус. То есть, если у нас всё покрыто, не знаю, юнитами, то если он загорелся красненьким, скорее всего, мы прямо на 100% знаем, где сломалось, что сломалось, как фиксить, вообще всё знаем. В случа случа случа случае высокоуровневых тестов мы этого не знаем ничего. Вообще заметь, что это не совсем так в моём случае, потому что когда я смотрю конкретно, что у меня какой-то стейт в базе поменялся, но я максимально знаю, то есть, может быть, конкретную точку, в которой это пошло, я не знаю, но там скоп гораздо уже, потому что мы проходим через Да. Дадада. Ну, смотри, потому что вот то на на что мы сейчас смотрим, это прямо вот самый верхний уровень тестов. Если мы спустимся пониже, ну, давай откроем вот DB. Это уже тесты для библиотеки, для базданных. Это уже не приложение. Но здесь есть интересная штука. То есть они, а, тестируются с реальными бэкэндами. То есть есть прямо честные юнит-тесты, которые никуда не лезут, а есть тесты, которые прогоняются на прямо разных разных бэкэндах. Мы поддерживаем кучу баз, и вот они все у нас там есть. И здесь мы вот можем посмотреть, что тесты вот а-а там DB, не знаю, там допустим, что там, ну, quyery схема сY, кстати, довольно высокоуровневая штука. Угу. Ну, давай тут quyilder посмотрим. Query builder тест. И здесь мы, э, вот, ну, вот, вот прямо вот такое вот собрали прямо, да, собрали. Вот вот должно получиться вот такое. Но quyilder - это не сильно интересно, потому что, а, здесь он не полезен. Он идеален. Query builder крут тем, что это вот прямо чистый чистая функция у тебя просто на фоне. Да, да, да. Ну, здесь здесь тестируется сам quyilder, да. Ну вот, да, оно вот вот такое вот. Есть вот посложнее штуки. Вот, допустим, есть тесты типа. Такого в на самом деле в базах нету. И мы здесь, э, запрашиваем quyer от конкретной конкретной реализации и, э, смотрим уже, а, что получится в конкретном каждом случае. Это у нас в других местах лежат сами вот детали этих тестов. Вот. А, и вот вот вот в этом случае мы а уже получим больше информации, что сломалось, где сломалось. Но опять же суперполной картины мы не получим, потому что это может сломаться, а, в том числе в самой СУБД. Наш код может быть не виноват, но вот в СУБД что-то поломалось или ещё что-нибудь такое. То есть есть много подвижных деталей и полную информацию, что вот сломалось именно здесь именно вот это давая идея фикси, чтобы прямо супербыстро. Это даёт только юнит. Причём, по сути, в твоём случае получается под каждый адаптер. То есть у вас ещё для каждого адаптера должны быть свои собственные тесты. Да, да, да, да. Ну вот если мы пойдём там, не знаю. Я, кстати, знаешь что не понимаю? Почему в каждом тесте создаётся создаётся парамс просто пустой, и я не вижу там наполнения. Может быть, я что-то не заметил. А они наполняются уже внутри в каждом адаптере. Тут такая взаимосвязанная немножко система. Есть базовые тесты, э, которые для всего, и они потом подключаются в каждый субпакет с драйвером и переиспользуется. Ну, такое немножко неявное, чтобы просто копированием не заниматься. Ты имеешь в виду, что у вас там прямо как прям на уровне шаблонизации, что ли, подставляются на уровне кросс-пакетных вот этих вот зависимостей? У нас ещё и тесты зависят. Понятно. Ну, ну тут тут интересно ещё сами workflow, да, это библиотека. Ну, здесь чуть-чуть всё по-другому. Давай посмотрим на немножко нишние всякие разные системы. То есть есть ещё вот что Bookstack, это вот WK система достаточно популярная, куча куча звёздочек, люди, да, да, дада. Вот зайдём к ним в тест и посмотрим. То есть здесь есть у них кучу кучу всего прямо дофига команды. Ну вот, допустим, AP мне нравится. Вот books AP. Что тут происходит? Вот это вот похоже на твоё уже. То есть есть NPIN и они как раз лезут ещё в базу. То есть они не лезут в AP, чтобы получить начальные данные там для первой книги, а делают всего вот один запрос. И потом смотришь, чтобы был ответ. Возможно, где-то они смотрят, что ещё и в басе что-нибудь там случилось. Ну вот, вот здесь вот, допустим, давай кate, да, креate или апдейт посмотрим, потому что там-то понятно. И, кстати, заметьте, они как раз прямо джейсончик разбирают и смотрят. Это вот та самая рукопашка, которую можно и желательно убрать, особенно тестов. Да, можно избежать. Это если они поэпе всё сделали. Если они что-то своё придумали, ну, извините, придётся ещё и валидатор свой делать. Вот здесь как раз что мы видим. Мы видим, что они сначала что-то там готовят, а, возможно, они там сходили в базу, ещё что-то сделали, потом делают как раз вот этот вот запрос, который может быть HTP, может быть нет. Скорее всего, нет. То есть, скорее всего, это вот вот этот вот аре дада, да. Что-то что-то внутри там фреймворк запихивается, получается ответ. Смотрит, что статус 200. А дальше они не делают ещё запросов. Они вот делают нам, а нет, делают. Ну вот, скорее всего, вот вот это вот Assert Activity exist, оно входит в базу, да? Даже выше. Они же, смотри, они как раз смотрят на new item по details имени, которое сверху, собственно. А, да, да. То есть они сходили в базу, но здесь нет. Они смотрят. Мм, ну, фактически это мой тест, вот как я тебе показал. Да, ну похож, да, похож. Очень похо. Они просто сделали, поскольку у них изначально просто объекта не было, они его новый создавали, поэтому они просто выборку сделали. Да. Здесь ещё идёт ответ по джейсончику. А у нас там не было, потому что мы смотрели с тобой не не опишку, а именно просто обычные странички. Угу. Во, рефреш, кстати. Букрефреш. А, ну я, кстати, что ещё хотел-то добавить по этому поводу. Для тех, кто вот видит, как я тесты пишу, чтобы это не вызывало ощущения, что там что-то Кирилл придумал. Это же не я придумал. Это на самом деле в вот вы заходите в документацию таких фреймворков, там так и написано. То есть я по сути среднестатистический рядовой формашлёб на фреймворке. И здесь мы видим, ну, фактически то же самое. Опять же, понятное дело, что ситуации бывают сильно разные, и это не значит, что у меня все такие тесты простые или только такие, но это большая часть тестов в типовых веб-приложениях. Обрати внимание, вот мы с тобой говорили по поводу в нормальных фреймворках урлы генерятся хелперами. Что здесь делает на как эта строчка for updates? Они тупо строчки собирают. А на чём этот букстак написан? Кстати, это это желавель. Равель, да. Но можно вот им накидать. Слушай, по-моему, они должны были уметь и также генерить их из урлов. То есть Ларвилы, я насколько помню, это это реально сделать. Вот. Да, да, дада. Да, там роутер доступен. Ты прямо просто его имя роута передаёшь и, соответственно, там функция rout что-то такое. Ты ей передаёшь эту штуку. У них даже, видишь, base and point. Ну, скорее всего, исторически так сложилось. просто никто менять не стал, и они, собственно, в этом духе продолжили дальше. Но в целом, кстати, вот если мы берём с тобой Open source, ну, нормальные чистенькие тесты, то есть с этим можно работать и спокойно жить. Нет, ну в принципе делаешь. Смотри, это смотря что за open source. То есть я в openourсе вообще достаточно мало видел, э, ну, готовых, скажем, систем. То есть вот система типа Вики там или ещё что-нибудь такое, это нетипично, в принципе. Вот. А, и в Open source такое нечасто. Обычно либо это правда, либо обычно это какие-то либы, а вот вот такие тесты они обычно совсем в другом месте. Они а в соответственно м по коммерческих каких-то закрытых приложениях и вот этом вот всём. Угу. Это правда. Это, ребят, вот, наверное, красные линии проходят сквозь наш весь разговор. Всегда надо это учитывать, потому что если вы применяете эти подходы глобально, это неправильно, они вообще не работают в либах. А в либах подходы, если вы примените на прикладные, вы просто себе создадите ад из тестов. То есть вы, конечно, будете классно всё тестировать, но вы задолбаетесь проект писать в таком стиле, хотя можно. Кстати, assert activity exist book delete. Заметьте, это они кастомный assert написали, который, видимо, проверяет какую-то там у них как раз табличка активности и что-то такое, да. что произошло некое событие, удалилась книжка, видишь, вот последний буквально определите. Да, да, вот, вот здесь. То есть они даже не проверили, кстати, что этой нету записи в базе. Они вот именно события проверили. Угу. Немножко понадеялись. Кстати, это тоже хороший показатель, знаешь чего? Что идеальных тестов не бывает. Ну вот, э, то есть логично было бы, да, проверить, что всё-таки нету записи в базе. Ну, опять же, это можно разными способами сделать, в том числе попытаться там пойти на эту страничку, посмотреть, что 404 она там даёт, если говорить про подход, который вот ты предпочитаешь. Они вообще косвенно это проверили. Они проверили, что в табличке активности dele. Ну, и типа мы считаем, что, значит, произошёл реальный Delт. К вопросу о том, что, ну, вот тесты, да, в реальной жизни так и пишутся. Ну, так, ну примерно так, да. У меня такие такие же примерно есть в коммерческих проектах. Это нормально. Вот. Нуно, а если мы сюда пойдём, то, скорее всего, здесь э будет и юнит-тесты. Вот, допустим, IPформаatр. Эта штука сложная, её тестировать вот таким способом не очень круто. И здесь вот, ну, классические совершенно юнит-тесты такие у нас тоже есть для всяких там расчётов, там ещё для всяких вещей. Вот. Вполне себе, да. Ну, это по сути чистая функция. Это тут вообще не бывает, мне кажется, разногласия. у тебя сложная внутренняя, они бывают, они бывают разного уровня. То есть бывают вот чистые функции, а бывают и не совсем чистые функции. Бывают для целых там модулей, бывает, что они пишут в какие-то стореджи, что-то, то есть по-разному. Ну, про IPформатор я имею в виду, тут прямо всё однозначно понятно. Да, да, да, да, здесь всё однозначно. Тупая, как дрова, это просто форматор. Тут вообще ничего нет. Обра. И, кстати, смотрите, может быть, у них где-то ещё какие-то есть тесты аля такие более низкого уровня, которые не запросы делают, потому что здесь мы сыли буквально шесть файлов. М, сортировка может быть. Нет, здесь урлы, активности. Вот. Уверен, что урлы тоже урлы. А заметь, кстати, тут уже по несколько запросов выполняется. Да. Дадада. Да, пермишены. Вот это может быть интересно. Может быть, полиси какой-то тестируется. Да, кто-то. А, я знаю, что они сделали. Они тестируют урлы, но они тестирую их с точки зрения доступов. То есть тоже, кстати, бывает разный подход. Бывает, что в тех же местах, где контроллер, а бывает, что их выносят, да, чтобы независимо было. Тут не всегда урлами это можно проверить, потому что комбинаторный взрыв происходит и слишком много этих урлов. Ты просто все эти тесты не напишешь. Так тоже бывает. У меня был, знаешь, как у нас был кейс? Э, мы в какой-то момент у нас было очень много ролей, и мы прямо, знаешь, что написали? Мы написали генератор тестов, который берёт комбинацию всех вообще возможных этих штук. Ну, не всех, но там всех топовых. И он фактически тесты генерировал через урлы, но на то, чтобы это проверять, не руками, потому что мы задолбались. Ты слушай, так нормально, но а не всегда тебе нужно проверить всё. То есть профессиональные тестировщики, они, в отличие от нас, э, немножко умнее в этом плане. Они находят такую штуку, как класс эквивалентности. То есть они исключают гигантские шматы вот этих вот комбинаторных тестов и выбирают из них только те, которые прямо реально отличаются и которые точно там сверят и будет раз разница какая-то. А во всех остальных случаях её не будет. Ну мы как бы по дефолту подразвиваем такое поведение. А что-то я ещё хотел по поводу тестов сказать, какую-то штуку. Кстати, ты сторонник? Нет того, что во всех этих тестах же по сути вот кто смотрит, может быть, не обратил внимание, тут же базы нет нигде, потому что в Ларавеле по дефолту работает так же, как и в рельсе, и как в джанге в той же самой. Транзакция начал, откат, соответственно, всё как бы автоматично. А ты бы, если бы писал, у тебя бы такой же был подход или нет? А, смотри, я пробовал по-разному писать. А здесь всё упирается обычно вот именно транзакции использовать и так далее. Оно вызвано тем, что с производительностью у нас есть достаточно жёсткие ограничения. То есть тесты, которые выполняются у нас полчаса, они нафиг никому не сдались. То есть их и вся нормально не сунешь, и локально не позапускаешь. Ну кто-то вот в своём уме будет всё это дело ждать. И здесь получается, что что а вкатить какие-то там фикстуры, там миграции и так далее, привести базу в начальное состояние, запомнить его, потом провести тест и быстро откатиться до вот этого вот начального состояния, это вот прямо вот база, чтобы сделать быстро. Достигается это дело, а двумя способами из того, что я знаю, а именно это первое, это вот как раз транзакциями и их откатом. по завершению теста. И второе - это, если у вас, допустим, пазгря, то это шаблонами делается шаблон базы и потом после каждого теста удаляется текущая база и с шаблона поднимается новая. Тоже, как оказалось, [ __ ] как быстро. Примерно на уровне вот этих вот транзакций. Да. Да. Мы, кстати, вот в том посте, собственно, в комментариях там люди как раз про это многие говорили. И, наверное, в их случае это нормально. Просто это не нужно. Там, где, грубо говоря, у тебя экосистема из коробки умеет это делать на уровне самого фреймворка, да, потому что, ну, типа это вот есть совсем ничего. Нет, наверное, такой подход нормальный. Я, кстати, до этого поста, вот, когда в комментариях об этом написали, вообще не слышал, что кто-то так делает. Оказалось, ну, мы с тобой видели, что люди реально периодически Да, да, да. Так, так, так делают тоже. А по, кстати, по поводу Давай сейчас тогда прямо на секунду отвлечёмся. У нас, видишь, тут легко переключаются сцены. Вот сцену мою. Ещё раз покажу. Я как раз тебе хочу показать, собственно, вот эти вот фикстуры, то, как это работает. Там есть, опять же, наверное, ты знаешь, но я для, так сказать, зрителей чуть-чуть объясню. И недавно, кстати, обнаружил, что интересно, вот эту вот систему с фикстурами рельсовы, её скопировали в GO, и она там довольно популярна. Это не те фикстуры, которые в Питоне думают, что фикстуры, это фикстуры именно данные специально для базы. Вот типа описания. А здесь есть, короче, прикол. В чём? Значит, есть вообще папочка с текстурами. Здесь, по сути, под каждую модельку, даже под каждую табличку описаны данные, и они туда вставляются. Причём они со связями. То есть здесь именно язык такой прикольный, потому что вот, например, cot version эликсир - это ведь не просто слово, это название сущности в другом таком же ямал файлике. А каким образом они соединяются? Они соединяются именно по вот ключам. То есть у вас именно ключи, но возникает вопрос: а какой адишник будет в базе? Как это работает? А как зависимости вставляются? А они сделали гениальную вещь. У них как бы каждый ключ, вот это, имя, оно через какую-то хэш-функцию превращается в айдишник в базе. И они их не просто автоинкрементом вставляют, они вставляют именно этим айдишником. Поэтому получается, что здесь у тебя есть зависимости внешние ключи, даже даже как бы двунаправленная связь, когда есть сущности друг на друга ссылающиеся, это работает, потому что он их последовательно вставляет с гарантированными айдишниками. Слушай, у меня сейчас точно так же тесты организованы в одном из коммерческих проектах. Ну, в принципе, это нормально. Да. И вот эта штука, она как бы, кстати, по дефолту в ресе такого нет. А он, знаешь, как делает? У него есть две механики. Первое - это перед стартом всего сюта он их в базу загоняет. И второе - это а в каждом тесте, вот как только ты там, там есть такая функция, типа, например, blлокпостs, а он у тебя понимает, что это посты, и он в этот момент загоняет их в базу. И это тоже долго, даже несмотря на то, что у тебя фикстура готова, потому что самое, на самом деле, долгое в этих тестах, как ни странно, многие, может быть, этого не знают, но это именно загнать в базу начальный сетап. Это даже не создание данных в процессе, как правило, оно маленькое. И знаешь, нам пришлось небольшой хак написать. Там прямо такой модуль компактный, который отвязывает, грубо говоря, от рельсы загрузку фикстур. То есть когда ты к фикстуре обращаешься, он просто идёт в базу. И отдельно у нас есть таска, которую мы запускаем перед стартом тестов, которая сразу это прогружает. Поэтому, видишь, нам шаблоны никакие не нужны. Это просто дефолтное рельсовое поведение. Мы загнали в базу все данные, и у тебя как бы транзакция каждого теста, она микроскопическая, потому что у тебя по сути только а то, что в контроллере было добавлено, оно и откатывается назад. Работает, ну, просто очень быстро. То есть у нас весь ЮТ, по-моему, знаешь сколько? 1.000 осерт. Нет, там тестов 700, что ли, что-то в таком. Ну, понятно, прикладная штука. Их меньше, чем чем у вас в, естественно, в фреймворке. В этом плане они вот все примерно такие там или 900 их. Короче, это всё проходит за 2 или 3 минуты. Вот что-то в таком духе. Ну очень даже. Очень даже. Единственное, что, смотри, у тебя состояние базы, до которого ты откатываешься, оно, а, в случае того, что ты его накатил целиком и полностью, оно будет вот одно. А если ты под каждые там группу тестов или так далее применяешь что-то своё, то у тебя ещё и кушается время на вот это вот применение. Не совсем, потому что обычно создаются, знаешь, как делается? У нас вот тоже фиксур очень много, и в какой-то момент мы поняли, что мы просто помрём, потому что делать фикстуру, которая, знаешь, во всех кейсах участвуют, слишком сложно. Ну, типа давай сделаем курсы пользователя, которые вообще там почти во всех процессах участвуют на сайте. Тебе потом очень сложно воспроизводить разные ситуации. Поэтому мы в какой-то момент, знаешь, к чему пришли? У нас внутри, грубо говоря, есть пользователь, и с ним связанный набор фикстур по какому-то кейсу. Вот я сейчас прямо даже покажу. Вот если мы в юзеров зайдём, вот буквально недавно делали там, а это другой проект, сорри, я думал, что я Hexlet показываю. Здесь этого нет, поэтому здесь такие туповатые названия. Но если вот посмотреть именно на Hexlet, а там видишь вот user как бы, например, ready to start learning. Это некий такой набор кейсов, когда он типа зарегался и провёл определённые процедуры, и дальше у него с разных сторон тут у него модалочка, тут он может делать, тут не может и так далее. И у тебя получается, что есть как бы фикстура вроде одни загружаем, но у тебя в рамках них есть такие графы, которые отвечают за какой-то набор сценариев, связанных, как правило, с пользователем, чтобы не не иметь разной базы. Ты просто как бы в этом тесте ты залогинился под этим пользователем, пошёл по этим сценариям, в этим под этим пошёл по таким сценариям. То есть вот так мы решаем эту проблему. Если я тебя правильно понял, сейчас скажеш, что я вообще не о том, не совсем. Слушай, смотри, то есть вот дело в том, что когда у тебя откатывается транзакция, она откатывается до какого-то стейта. Если у тебя начального, да, да, да. Если у тебя начальных стейтов много, то тебе и соответственно надо как-то между ними переключаться, и это уже транзакциями не сделаешь. А начальных стейтов много, опять же, видишь, мне кажется, как будто ты исходишь из того, что есть какие-то взаимоисключающие стейты. У нас такого нет, потому что у нас начальный стейт, как правило, пляшет от пользователя вот в нашей системе. То есть, грубо говоря, вот, э, ну, почти всегда можно так упростить систему, грубо говоря, что вот если ты логишься под этим пользователем, то его начальный стейт и то, что вокруг него, будет именно его. А, ну для каждого пользователя это всё равно у тебя получается разный начальный стейд. Ну, например, курсы, в которых он находится, подписка, которая у него была, биллинг, история. То есть, да, я имею в виду, в в базе оно независимо лежит или это гигантский стейт на всех юзеров? Просто залогиниваешься под каждого разным. Да, это общий стей, то есть суммарно это одна база, это один стейт просто. А не, ну тогда никакой разницы. Совершенно никакой раз. Жения же нету здесь. Да, просто я видел немножко другую штуку, что под группу тестов есть э какой-то свой стейт, сове��шенно свой стейт, и он поднимается отдельно. А, я понял. Зна, наверное, если бы у нас были ещё больше объёмы, ну, типа там миллионы строк кода и так всё сложно, что было бы невозможно, наверное, мы бы до этого дошли, но мне кажется, мы как проект никогда до такого не дорастём. Нам гораздо проще просто данные внутри одной и той же базы, просто фикстуры как бы вот немножко отдельно. Мы по сути по именам делим, что у нас есть вот определённое именование, оно делает эту группу отдельной. Вот. А, кстати, вот то, что ты у тебя есть пример, мы можем здесь это посмотреть. Как они Мы, кстати, вот посмотрели в этом проекте, который ты сейчас показывал или другой проект, что они их используют. А как они их создают эти данные? Потому что мы их не видим. Но вот здесь вот, например, есть какие-то фикстуры, то есть здесь есть данные и, ну, вот, допустим, entries. И здесь очень похоже. Вот в чём здесь [ __ ] Сразу это видно. Вот здесь в чём им приходится указывать айдишники полностью ручками. Смотри, в чём кошмар. А из-за того, что это вот не та система, которая объяснил, где ключи автоматически транслируются в идентификатор и он используется. Им, по сути, пришлось прописать все идентификаторы ручками. И, грубо говоря, глядя сюда, ты не можешь легко и быстро, ну, понимаешь, да, загрепать ту штуку, на неё перепрыгнуть и понять, о чём идёт речь. Ну, представляешь, у тебя Ну, потому что у тебя какая-то фигня с идентификатором единичка, да? А в этом плане, да? А у меня же А, да, да, да, да, да, да, да, да. Ну, это вот конкретно здесь так такой такая стиль. Не знаю, я у себя референсы делаю. То есть Ну, это потому что ты сам ручками это сделал, а потому что в Да, не, это какая-то стандартная система, по-моему, то ли симфонёвая, то ли ещё что-то. А в симфоне это есть. Я просто это даже не в Симфоне, это отдельная какая-то штуковина, которая в симфоне одно время как дефолтную поставляли. используется. Просто те люди, которые привыкли загонять данные в базу SQэлем чистым, а у них, ну вот это они и делали, да, у них есть эта проблема, потому что если у тебя вообще нет идентификаторов, вообще [ __ ] потому что тебе их надо получить, а потом вставить, то есть у тебя SQL превращается в такой адовый вообще систему, чтобы всё это создать. Либо приходится вставлять айдишники со всеми вытекающими последствиями, потому что вам придётся в голове всё это проставлять и делать. Так что, ребят, если вы так делаете, вот, может быть, это то, чему вы здесь полезному научились в этом видео, посмотрев его, и делайте теперь по-другому. Напишите обязательно в комментариях. Очень интересно, кто столкнулся с этой проблемой. Ручками всё это разруливает. Да, да, ручками разрулить не надо. И, кстати, вот айдишники, которые вот такие интовые ещё и скорее всего и в базе автоинкрементно, это со временем я понял, что это большое зло. И всё это дело очень сильно усложняет всю нашу разработку. И генерить UU ID на клиенте, а потом совать в базу уже готовые, это прям сильно всё упрощает. То есть вы так делаете? Да. Да. В текущем проекте я делаю так. Всё, всё с клиента, всё как бы UID версии седьмой генерится и всё. И нормально. А ты можешь в двух словах сказать, какую, например, проблему для тебя это решило? Потому что я бы не сказал, что у меня есть что-то. Слушай, я, а, когда я начал знакомиться с DD, я прочитал это дело, вдохновился, мне прямо дико понравилось, я кайфовал от этого, но тогда я использовал автоинкрементные ID в базе, и у меня был, э, ну, допустим, там блог, пост, комменты, да, по DDD я должен взять, построить полный агрегат и потом всё сохранить, допустим, там пост с комментами или там пост с картинками или пост там ещё с чем-нибудь, да? А я так не могу сделать, потому что у меня есть айдишники, и пока я не сохранил, я этот айдишник не знаю. И вот эта вот проблема у нас офигенно решается с помощью UID там четвёртой или седьмой версии. Четвёртую использовать теперь не надо. Лучше, конечно же, седьмую. Ну это по сути вот как я рассказывал про фикстуру, да, когда у тебя даже если кросс зависимости не возникает проблема, потому что он фактически сразу генерирует айдишники и всё получается. А я не могу не заметить то, что по крайней, что в рельсе, как ни странно, несмотря на то, что это вроде бы Active Record, это работает. То есть, если я создам сущность и зависимости туда сразу прокину, и при этом оно не сохранено, то сейв, как ни странно, сохранит весь агрегат. Правда, единственное, по-моему, там на уровне связи надо дополнительно как-то это указать, что надо так делать. И плюс там ещё есть такой прикол, что валидации можно по-разному вешать. Можно валидацию вешать на наличие сущности, а можно валидацию вешать прямо именно на наличие идентификатора. Это как раз в одном случае сработает, а в другом случае не сработает. Вот мы этим, кстати, пользуемся активно. То есть у нас вот вот такая штука есть. У Да. Так, ну хорошо, давай. Мне кажется, вот с этими тестами более-менее всё понятно. Есть там какие-то ещё, может быть, знаешь, не ну как хотя бы тесты, которые не идут через урлы. Тесты, которые не идут через урлы, наверняка есть. Так, это точно идут. Functional page render. Ну вот не идут через орлы. Ну он же log in s, то есть это 100% логин здесь. Нет, нет, не а да, да, это через орлы. То что иначе как-то ну юниты есть куча юнито. Вот дай посмотрим, что они под юнитами имеют в виду. И причём я понимаю, что давай чуть посложнее сделаем там вот сервисы. Во, сервисывай сервисы, потому что насколько мы считаем, что это да будет оно или не оно? Вот драфты, тест драфтов. Угу. Так, фикстуры. Во, похоже. То есть есть у нас базочка. Так, сущность забрали. Делает сетап. А что-то там фигачит, фигачит опять вот в базу сходили, похожеже. Ну да, видишь, айм, то есть они смотрят вот здесь по стей в базе, то есть делают какие-то манипуляции сервисов и стоит в базе потом спорт. А давай посмотрим вот, собственно, что непосредственно эта функция делает. Драфт что-то там вот она выше. Они создали какой-то черновик, да, на восемьдесят пятой с троке он применяется. Угу. Плайдрафт. А что там происходит в комментариях? Видно, что там ещё юмористы всё это писали. Так, адрала-ла. А берут у нас бер драфта и канонично его применяю здесь по сайту ID, структура ID, вот и всему остальному. Нормально логике, логике нормально тут. А события, короче, вот прямо вот по полной программе обрати внимание, что они внутри ещё какой-то сервис вызывают, который, в свою очередь, тоже что-то делает. И логики здесь [ __ ] Это как раз то самое, наверное, про что я говорил, когда у тебя, смотри, там удаление драфта data и set revision notes, то есть много много в разных местах, включая, кстати, тут откат транзакции и эксепшены и комиты. Вот это. Ну да, здесь достаточно много всего. Не, это уже точно не юнит. Ну не знаю. Мне мне кажется, что это совсем не юнит. Это что-то другое. Это это просто какой-то вот тест большого шмота от чего-то. Интеграционный, наверное. Ну, фиг знает. При этом это совершенно другой интеграционный. Ну, по сути, это тест-сервис, если так снаружи посмотреть. А кто-то тебе возразит: "Ну как же, Саша, сервис это и есть модуль. Ты же сам только что говорил". А нет, там внутри ещё. Ну, наверное, в этом смысле он модульный, но он совсем не юнит. Или ты его модульным не назовёшь? Да, я скорее всего нет. Не назову. Не назовёшь. Мне кажется, как будто мы ещё больше запутались в таком случае, я думаю. Да, просто, ну, вот конкретно вот это вот разделение между там модульным тестом, интеграционным, там ещё каким-то, всё это достаточно условно. Здесь есть как бы маленькие изолированные тесты. Ну, скорее всего, вот это вот все юнитами плюс-минусы называют. И есть те, которые вот куча движущися частей этого. Вот то, что мы здесь видим. Угу. Угу. похода в базу стей, но для меня модульные всё-таки это которые -э не зависит от внешних систем, которые мы не контролируем. То есть, если мы контролируем всё из того, что тестируем, то это, ну, для меня это вот плюс-минус там модульный тест. Если мы что-то не контролируем, там базу или там внешнюю систему, какой-нибудь поход куда-нибудь или там, а используется какой-нибудь, ну там супервнешняя какая-нибудь фигня, которая нестабильная, и мы из-за её поведения не ручаемся, тогда, наверное, это уже не совсем модульный тест. Давай для того, чтобы как бы этот для контраста закрыть, я тебе покажу, наверное, какой-нибудь тест, который давай у нас не является м ну таким вот, потому что я рассказываю, что типа вот вот такие простые тесты, все дела. Естественно, у нас такие тесты, естественно, мы всякое разное пишем. Вот сейчас я покажу. Ну вот, если даже посмотреть внутрь, а, и присмотреться к названию папочек, видно, что мы не тяготеем к названиям, э, там, модульные, не модульные. Тут просто вот есть тесты контроллеров, есть интеграционные, ну, интеграционные просто это опять же стандарты в, а, в рельсе, это так называют тесты, которые вот всякое разное зацепляют, но при этом это не через контроллеры идёт. Есть тесты на джбы. Опять же, это тоже к чему это относится, да? Там уже по-разному, в зависимости от того, что там вызывается, поэтому просто тесты на джобы, тесты на мейлеры, на модели. Ну и плюс вот у нас есть ещё мутаторы и сервисы. То есть слоёв-то у нас, кстатики побольше, чем обычно даже принимается. Просто я про это не рассказываю. Ну похоже, да, но в целом много, потому что там Ой, а здесь всего один тестик. Ну потому что обычно они тестируются как бы косвенно через сервисы чаще всего. Ну тестов сервисов уже побольше. И давай посмотрим, что у нас вообще в Юните. У нас есть ровно один юни, ровно один юнит, который мы называем юнит тестов. Юниттестом. Final Creade. Ой, слушай, а он не какой-то не юнит, по-моему. А я даже думаю, что он вообще случайно здесь лежит, тебе честно скажу, потому что ты видишь, что эта папка, она как бы не используется. Это, знаешь, даже возможно, с чем связано. Там со временем именования папок всё-таки проекту много лет оно менялось и возможно просто его не перенесли. А, типа растаскивали по смыслу, а этот не нашли смысла в этом тесте. Ну просто никто, ну когда его последний раз правили, ну типа 8 месяцев назад там и то какие-то дефолтные конфигурации Линтера меняли. То есть вообще непонятно, что это за история. Понятно, что скорее всего это бы лежало вот тут вот, потому что там моделька тестируется. Да. Да, скорее всего. И мы что-то внутри. Кстати, обрати внимание, просто такая приколюха. Это делает специальный гем annotation, который знаешь что делает? Он над каждой моделькой, над каждой фабрикой, над каждой фикстурой добавляет структуру таблички. Вот так вот. А зачем? Ну это супер удобно, потому что ты, например, смотри, вот я в фикстуру зашёл, я там наполняю, я забыл. Например, в прожектах я вообще не помню. А, стой, в прожектах почему-то пусто. Вот. Ну, я просто не помню, что там и как. И раз, вот оно прямо здесь всё как надо. А у тебя дшки нету? Ну, фикстура - это ям файлы. То есть для того, чтобы у тебя это под работало, это, конечно, нужна не просто там лспишка какая-то, у тебя прямо нужна глубокая интеграция твоего фреймворка в, ну, с базой, да, типа, понимаешь, да, да, тут даже не только с базой, это же это же Yamalфайл, то есть у тебя фреймворк должен банально знать, что это не просто файл, поэтому такое не очень люблю y файлы. Ну, ну фикстуры так описываются. И поэтому теоретически такое возможно в каком-нибудь руби майне, в котором реально они этим заморачиваются. Но, как ты понимаешь, ни в скоде, ни в каких других редакторах подобного нет. Это надо прямо сильно упарываться. А поскольку эта штука бесплатная, ну как бы почему нет? И поэтому она банально есть, ну она генерит просто вот для всего. И нам это супедобно, потому что ты зашёл, сразу увидел. Самое главное, что это не ручками делается, то есть мы это вообще не трогаем, а оно, причём запускается автоматом после каждой миграции. Вот так что вполне себе удобно. И оно, видишь, для моделей тоже генерится, соответственно. Ну вот тебе пример там. А мы посмотрели, давай тест, э там валидный телефон номера. А мы, соответственно, запихали туда телефон номера, ну, и посмотрели, что он правильно возвращается в правильной структуре. О'кей. Ну, это это тест, который и, собственно, базу в себя включает, то есть её работу правильную. На самом деле нет. Забавно, что базы тут нет. Там реально в модельке есть прямо нормализация. То есть он А почему эта нормализация не вынесена куда-то? Почему не вынесено? Ну, типа нормализатор телефонов. Было бы логично. А он, скорее всего, отдельно где-то и есть. То есть он, скорее всего, просто тут тупо используется. Это абсолютная правда. Сейчас я тебе скажу, если тут внутри посмотреть, а вот видишь, есть отдельная штука, которая там Phone Number, например. Угу. Угу. Да. И тут, кстати, как раз можно было бы сказать: "А что он типа отдельно не протестирован?" Ну, может быть, скорее всего, отдельно протестирован. Я тут хоть убей, не помню. Понимаешь, это же могло как быть происходить. Тут всегда важен контекст тоже происхождения всего этого добра. Может быть, вот изначально мы такие понимаем, у нас телефон только в ледах и его в А, ну та там он и было, а потом его уже вынесли. Да, да, да. А потом может быть такие: "А ещё где-то, ну давай вынесем". То есть я не при этом, естественно, я как программист сразу понимаю: "Ну, конечно же, это отдельная концепция, абстракция, там можно написать". Но в таком случае мне бы пришлось и там, и там писать, и поэтому, ну, а допустим, это был бы вообще какой-нибудь, не знаю, гем, ну, в смысле, готовая библиотечка, а мне это надо убедиться, что у меня лид работает, понимаешь, да? Потому что для меня это критическая модель. Лиды, собственно, это деньги, это и есть моя история. Поэтому, когда я это делаю, естественно, я в голове все эти кейсы есть, и дальше ты просто принимаешь решение вот в конкретном случае как поступить. А, ну а кто-то бы вообще тестов не написал никаких. Да, ты сам понимаешь, да, поэтому были бы у него критические случаи, да? Да, да. И поэтому вот оно так выглядит. А со временем, я тоже про это регулярно рассказываю, то есть вот, ребята, есть, когда я разбирал чистый код, и там, знаешь, Фаулер, ой, не Фаулер, а Мартин, он такую вещь ещё говорил, что вот у вас тест, это та, это ещё важнее, чем код, они должны быть идеальными, там вообще не должен быть don't repat yourself, а на самом деле там ровно наоборот, он repat yourself там не должно быть вообще никак. И типа за ними надо также следить. И я, э, когда разбор делал, я не знаю, ты смотрел его или нет, я там рассказывал, что, ну, это невозможно, если у вас огромное количество тестов, у вас всё начинает копипастый работать. Я когда в рельсы комит делал, да, у тебя там один файл, 10.000 строк кода. И каким образом ты эти кейсы в голове свои уложишь, когда тебе маленькую штучку надо поправить? Ты просто берёшь похожий тест, копируешь его и что-то там правишь, чтобы в твоём кейсе работало. Потому что Слушай, а это это не значит, что это штучка, про которую ты сказал, если у неё тесты на 10.000 строк, что она [ __ ] какая сложная и она не должна быть одной штучкой. Смотри, это тесты на всю подсистему на подсистему джоб. А там много файлов, но один из них вот такой, он большой, там очень много тестов. И конкретно и конкретно я знаешь что менял логику. Я менял логику. Там штучка-то маленькая, но она влияет не то что на всё, она как бы должна проверяться там. Это дефолтное имя очереди, там что-то такое было. Ну и, соответственно, это одна из вещей, которая вот туда попадает. И понятно, что а я же не могу понять все кейсы, которые они там, ну, понимаешь, да, делают. Это же, ну, слишком сложно. Это, блин, фреймворк целый, который там люди тестируют. Поэтому, если ты хочешь как бы участвовать то есть тут варианта два. Либо тебе говорят: "Иди нахер, разбирайся с тестами, учи всё наизусть, тогда всё, контрибьюторов ноль". Либо вот таким, как я, дают возможность типа: "Да, о'кей, чувак, мы это примем". Поэтому пиши, чтобы работало, чтобы оно вот в общей конве, но идеальных тестов ты, ну, не получишь. Понятно, понятно. Слушай, ну такое, да, с одной стороны, вот, короче, на 10.000 строк файлы мне не нравятся. Я бы это сами тесты разбил на несколько. То, что они тестят какую-то одну подсистему, не обязательно знать, что все тесты должны быть в одном месте. Да, там, естественно, не один файл. Ну, может быть, там 5.000 строк. Это такое часто бывает, может не 10. Но я имею в виду, что он просто на, короче, мой поинт был показать, что он настолько большой, что ты в голове это уложить не можешь. А, естественно, там не один файл, там, блин, папочки с кучей всяких разных файлов для разных ситуаций и всего остального. Там только фикстур одних, знаешь, сколько разных видов д. Ну, короче, когда у тебя во фреймворке миллионы строк-кодов, у тебя не может быть мало. Неважно, один этот файло. Нет, тестов не может быть мало. Они могут быть организованы по-разному и на разные там части этой подсистемы могут быть отдельные, например. Ну это опять же такой дизайн, а тесты писать это всем нам очень сильно не нравится, поэтому и получаются такие стрёмные стрёмные стрёмные штуки. Смотри, вот, кстати, очень такой простой тест, который реально относится к модельке. Обычно, кстати, мы такие тесты не пишем, то есть редко. Видимо, может быть, был баг какой-то, из-за которого пошле написали. Смотри, что здесь происходит. Здесь lesson как бы опрувится после. Ну да, да, дата не проставлялась, я так понимаю. Дадада, видимо, да, потому что очевидным образом state approved он как бы проставлялся, но ну но видимо для дополнительной истории. И соответственно проверили, что дата проставляется. Вот ты считаешь это юнитом или не юнитом? Как ты вообще-то относишься к такому тесту? Ну это не юнит, потому что у вас эта штука тестирует что-то, что лезет в базу Active Record. То есть, ну, юнитом это, на самом деле, не назвать. Если бы оно в базу не лезло, наверное, это был бы юнит, но оно лезет, да. Но тут возникает другой вопрос. А а надо ли пытаться, чтобы это не лезло в базу? Вот учитывая, что экосистема так устроена, не надо париться, да, я вот в этом случае не надо, потому что ну как бы ты тестировал, что у тебя записывается в базу. Зачем отвязывать от базы то, что ты ты хотел же проверить, что оно в базу пишется и читается, а не то, что оно просто куда-то там в память пишется. То есть этот тест, он нормальный, он тестирует ровно то, что ты задумал. Ну и давай, наверное, последняя вещь, которая вот уже такая менее типовая, когда, как я и говорил, там всякие истории у нас с докерами-шмокерами. Это не единственная история, но просто покажу, потому что здесь уже всякое разное исполь на самом деле. На самом деле там подмена генерируется. То есть, что здесь происходит? Оно прямо всё в себя включает. У нас есть определённый, причём, заметь, это не в сервисе лежит, потому что здесь гораздо больше всякого разного. Смотри, тут и джоба запускается, и билд запускается. То есть это что вообще такое? У нас м как устроены практики на хекслете? Это каждая практика - это отдельный репозиторий. Поэтому у нас их там типа 10.000 репозиториев только на практике. Ух ты, каждый каждая практика, ну это потому что отдельный тест, это отдельный сетап, это всё отдельно. Плюс каждая практика, каждый комит - это ещё и новый образ. А, я понял. Опять же, очевидным образом. Понятно, чтобокс поднимается и там что-то происходит. Угу. Каждый комит новый образ. Почему? Потому что на самом деле Хекслет устроен довольно интересно. У тебя люди, которые проходят прямо сейчас какую-то практику, а они проходят ту версию, которую они стартанули в тот момент, когда они её стартанули. Там есть кнопочка в редакторе сбросить, которая типа тебе ставит. Новый, кстати, редактор, там две вещи: новый редактор и новую практику. Поэтому получается, что, грубо говоря, у тебя одновременно может быть такое, что люди проходят много версий практик, а вот если ты с нуля стартуешь, то у тебя стартует новое. Таким образом, мы позволяем, мы сделали механизм, который позволяет нам очень легко и быстро двигаться, меняя все практики. При этом не делай так, чтобы пользователи нас ненавидели, потому что он прямо сейчас запускает проверить, а у него там уже что-то поменялось. Вот. И вся эта машинерия, она, по сути, превращается в полноценный CCD механизм, потому что там есть всё. И это свой, потому что, ну, он не укладывается в стандартные какие-то истории. То есть у нас программное управление тем, что пришёл комит, надо сделать сборку. Сделается сборка, там, значит, нотификации, тра-та-та-та-та. Это всё выкадывается на сервак. Там это тоже целая история, как оно выкатывается. Дальше идёт запуск и, соответственно, там ещё куча механизмов с запуском контейнеров на удалённых тачках. Причём делается это на кластере, то есть там не в смысле одна машина, там, чтобы ты понимал, там консул участвует, потому что там сервис discoverovery, чтобы понять машину с меньшей нагрузкой. Это к вопросу о том, что когда вот люди смотрят на Хексле, да, они такие: "А, ну подумаешь, тут крудики делают". Нет, вот эта штука, я с ней даже надо доклады делал много лет назад, потому что она сложная и она хитрая. Она там, чтобы ты понимал, какие там технологии, там трафиic engining, там кластер из машин, удалённое управление докером, а консул как сервис Discoverovery, куча башевской всякой истории запуска машинерии. Ну и ещё там есть всякие истории, связанные с бэкапами, очистки после этого добра. Вот в двух словах. Угу. Угу. А, слушай, ну то то, что он бил билдится на каждой комиде, на самом деле для меня это немножко такая прямо новость. Я бы так не делал, на самом деле. То есть я бы делал какое-нибудь revwрело, да, я понимаю, какую решает проблему. То есть как бы отсутствие лаврелода там, где его нет. А у тебя не знаешь, в чём главная проблема, что технически невозможно то есть представь, человек в середине написания кода, и ты такой ему: "А у вас задание поменялось?" То есть ты никогда не знаешь, комит меняет что. Он меняет целиком задания, он меняет описание, он меняет тест или меняет решение учителя. А, ну вообще, да. Если я поменял, что Да, там же взаимодействие. Угу. Да. А если я ему сказал, что у вас А давайте мы в этот проект ещё, в это задание ещё посгрес воткнём, понимаешь, да? Это тебя люди просто ненавидят. Крутая система, полное версионирование получается стейта прямо. Это это мегакрутая штука. Мы даже из-за этого, знаешь, во что упирались? Ещё когда старый был адаптер старый до Оверлея у докера была файловая система, мы упирались в количество слоёв. То есть у нас так много было слоёв, что нам приходилось решать, короче, какие-то невероятные задачи, которые обычные люди не решают. Мы там в иноды упирались, ещё всякие штуки. Так, ладно. Короче, я просто что хотел сказать, что вот при этом, если ты посмотришь, тест не то чтобы выглядит сложно, а что мы делаем? Мы запускаем, собственно, этот билд прямо полностью целиком. А дальше мы, кстати, смотри, промежуточное, поскольку это полный тест вот этого всего кейса, мы смотрим. Ага, а в билде там всё, что надо было сделано. То есть редха появилась, там какой-то ключик, что что это запушено и что бифиниш. То есть это как бы запу не означает, что билд закончился, да? То есть там разные состояния, их много, это сложно. И, естественно, здесь этого не видно, потому что здесь вызывается просто сервис напрямую. Тут никакого DI нет, но внутри di, он как сервис-локатор скорее работает. Поэтому в реальности, конечно же, мы в докер не ходим. И там прямо целая куча стабиков, которые вот это добро всё подменяют, просто чтоы не делали тяжёлые операции. Поэтому это всё работает очень быстро, да. А это уже как бы непосредственно запуск. То есть мы запускаем все штуки, которые уже в базе смотрят, чтобы всё это было собрано. И кстати, чтобы ты понимал, там ещё есть прикол, когда стартует контейнер для удобства отладки, там на каждой F5 он стартует заново, потому что у тебя следующий запуск может оказаться на другом серваке, если текущий умер. То есть там вообще, говорю, хитрая система. И поэтому для того, чтобы было удобно, легко отлаживать, в случае, если какая-то фигня, у тебя на каждый F5, на каждый запуск создаётся запись, которая называется Run. То есть это конкретная конфигурация контейнера. Вот конкретно для этой ситуации, понял, понимаешь? Да. Поэтому поэтому это табличка с сотнями миллионов записей. Она такая приличная там и растёт. И, ну, её, по крайней мере, чистить можно безопасно. То есть мы типа всё, кроме карантов дропаем. Старые эти раны можно, да? Да, старые раны. То есть ис - это запуск на конкретной машине, а есть ещё ран - это непосредственно запуск контейнера, потому что 30 минут неактивности контейнер как бы кладётся. Опять же, ещё одна система, которая есть, чтобы не перегружать сервера. Вот неплохо. Вообще про это как будто нужен отдельный, знаешь, этот доклад на тему того, что мы там сделали. Кстати, сделали, если не в шестнадцатом или в пятнадцатом году. С тех пор почти не менялась эта штука. Поэтому смотри дальше мы его стопаем. Выполняем Джобус. А по перезапуску обрати внимание, кстати. Хм. А нет, стоп. Ну ладно, я я давай сейчас не буду как бы вспоминать, почему и как это всё происходит, но мы в конечном итоге заканчиваем архивом. И архивация - это знаешь, короче, какая фишка есть, когда ты, грубо говоря, заканчиваешь, ну, заканчиваешь, что такое? Это ты перестал сейчас этим заниматься. То есть, может быть, ты не закончил задание. У. И у нас там такая фигня, что если ты не активен, надо это закачать в эстрижку, потому что в следующий раз, когда ты захочешь это же упражнение начать, например, завтра, может оказаться, что текущий сервак надо развернуть обратно. Ага. Да. И поэтому мы, если это тот же сервак и папочка там есть, мы как бы ничего не делаем. А если мы видим, что это любой сервак, мы во время старта мы такие: "О, о'кей, пошли скачать со стрижки и развернуть твой код". Вот. А были, кстати, приколы, когда эти архивы были гигабайтные по размерам, поэтому там ещё есть механика, которая пытается понять, типа, нет ли тут логов и чего-нибудь лишнего, чтобы туда это всё не улетало. Подреми, да, неплохо. Крутая штука. Смотрим и смотрим, что он архивирован. Да, и это тоже не юнит. Это уж точно не юнит. Это не юнит. Это не юнит, да. А ты упомянул про 5% вот суммарно. Вот если слушай, а в каждом проекте, да, в каждом проекте, в принципе, есть ээ два разных домена. Это как раз вот из, не знаю, DTD термин или куда-то ещё выше, там из продуктовых всяких вещей. Есть, а, specific domain, есть generic domain. Вот Generic - это всё, что в принципе наш бизнес не делает уникального. Это хрень всякая. Это авторизация, аутентификация, какие-то админки, круды, вот эта вся хрень, которую мы можем заменить вообще на всё, что угодно. Мы можем это заутсорсить, мы можем взять openсорсик какой-то, мы можем там, не знаю, WordPress поставить нафиг, чтобы он там сайты наши сервел, и ничего плохого не случится. А, да, Тильду, Тильду нормально вообще. А есть как бы кордомен, это вот то, что ты показал, это как раз кордомен - это то, что за нас другие не сделают. Это вот прямо вот ядро нашего сервиса проекта, то, что он делает уникальное. И вот на это как раз, ну, вот эти вот самые странные прикольные тесты и пишутся, на самом деле. Я знаю, а у нас-то он ещё такой, опять же, понятный для веб-разработчиков, потому что, ну, как ни крути, всё равно, ну, понятно, что здесь происходит. А я вот слышал про ребят, которые виртуализацией занимаются, какой у них там ад, чтобы это всё разворачивать операционки зависит и так далее. мобилки, там тоже много весёлого разного. И люди, которые делают даже не сложно с точки зрения бизнес-логики, потому что бухгалтерия, например, да, бизнес-логика сложная, но по сути это просто алгоритмическая часть, там всё равно те же самые таблички и базы. А, наверное, что-то, что вот какое-то вот внешнее с или ну что-то не, наверное, за рамками веба. Слушай, с мобилками вообще труба. На самом деле я-то был мобильным разработчиком. То есть в одном из проектов, я там был что-то в районе 6 лет на одном проекте, на Норвегию мы работали, делали travel сервис. И я изначала изначально был веб-разработчиком там. То есть я пилил бкэнд, иногда фронт чуть-чуть. Вот. А потом а что-то мы решили, надо мобилу развивать, прямо очень надо. А мобильных разработчиков что-то у нас мало. Вот. Ну и на вебе всё сделано, всё вроде работает. Ну и меня перекинули туда, короче, на мобилу. Я немножко подофигел поначалу, что всё другое и всё по-другому. И вот там с тестами было весело. То есть это был тот этап, когда у нас ещё не было, а, 10000 андроидов, которые друг на друга похожи как копии. У нас, э, ну, был iOS, который отличался, конечно, но андроиды были все разные. Были самсутвские андроиды, были другие андроиды, и все они работали в разнобой. Кто как, кто пилил свой Google Play, кто ещё что-то делал. Единственный способ нормально протестить, что твоё приложение работает, это было сделать либо ферму, либо купить сервис, который делает эту ферму. То есть у тебя лежали просто вот реальные телефоны с разными версиями софта, с разными там железками разных модификаций. И вот эта вот вся хрень, она у тебя на столе от одного твоего ноутбука запускала дофига билдов на всех телефонах и на каждом его экранчике ты просто вот глазами вот так смотрел. Нормально вроде работает. Я слышал много про, да, мобильные тестирования. Насколько это ад, но я так понимаю, что сейчас, кстати, у них там вроде продвинулось, да? Да, да, да. Сейчас есть, э, дофига сервисов, которые умеют эмулировать прошевками там или ещё как-то куча куча вот этих вот особенностей самих устройств. Апловские туда входят или всё-таки эпловские тоже туда входят? Ну, эпловские вообще легко тестировать, на самом деле. То есть у них вся линейка эпловских девайсов. Ну, сейчас, наверное, их больше, но это всё равно очень конечное такое количество. То есть, если андроидовские девайсы все взять, это будет просто вот нереально всё это дело осилить и руками проверить. Вот. А ты ещё упомянул про Паулера, кажется, который говорил, что там наши тесты должны быть супер идеальными, а наш код там может быть каким угодно. Мар в чистом коде. В чистом коде. в чистом коде это говорил. Он это говорил, ну, скажем так, тогда, когда он это говорил, это не было актуальным. Сейчас это более актуально, потому что у нас есть наши замечательные друзья лмки. И лэмкам я бы я бы не доверял писать тесты, потому что они не знают, что мы хотим. Они как бы бездумные генерики следующих буковок. Ну, плюс-минус. Вот. Аа код они как-то там навайткодить могут, но проверить они его могут только тестами. Если у нас тесты идеальные, то, в принципе, можно наш код подогнать под эти тесты, а наоборот особо не сделаешь. Ну это если исходить из предположения, что ты просто говоришь протестируй эту фигню, а не ты его просто просишь написать тесты, которые те сценарии, которые тебе нужны. Потому что если он пишет то, что тебе надо, это немножко другое. Это такой, да, это это разные уровни. Я имею в виду, что можно авто написать код и несмотрять на то, что он там реально делает. Ну, конечно, вот с безопасностью там будут большие вопросы, но то, что оно будет работать и работать согласно твоим тестам, это можно сделать. То есть мы делали эксперимент у себя, а опять же DI контейнер брали, штука очень сложная. И у нас в Е3реть у нас стопроцентное покрытие тестами практически в каждом пакете. И вот с DI контейнером мы делали прикольный эксперимент. Я брал достаточно ранние версии Винсрфа. А там были бесплатные кредиты какое-то время, то есть я туда, а загружал вот этот вот DI контейнер. У нас есть тесты. Я говорил: "Тесты не трогай ни в коем случае". код правь. Напиши мне бенчмарк, который, ну, типичные там всякую фигню в контейнер закидает и, соответственно, попробует повыбирать. А, и потом попробуй погоняй бенчмарк и на каждой итерации пытайся ускорить DI контейнер и смотри, чтобы не сломались тесты. И вот эта штука у нас, ну, я три, трое суток запускал эту штуку, съел, короче, все бесплатные кредиты, потом ещё и денег там нормально отожрал. Просто интересно очень было, что получится. А 3 ночи оно работало, жрало токены, как не в себя, а получилось три ну вот таких вот набора изменений. А в одном случае получился полный бред, который ни фига не ускорил, и раскидал просто код в какое-то непонятное мясо. Но тесты проходили, кстати. Круто. Вот. А это вот о том, что код может быть не идеальный, но он может как-то работать. Вот. А вторая штука ускоряла всё процентов на 30. Мы немножко офигели. Мы читали этот код неделю, пытаясь понять, что же оно сделало. Разобрались, пару-тройку трюков оттуда забрали. Где-то 25% ускорения мы добились. Остальные 5% мы не стали применять, потому что код превращается в полный трэш. И потом это просто вот мы ставим крест на том, чтобы кто-то туда читал его и контрибьютил. Нам такого не надо. И ещё один, а, короче, мы делали, и там ускорило прилично, но оно в такое превратил этот код и такие дико неочевидные решения, что мы, короче, чуть ли не месяц пытались к кном подступиться с разных сторон и сдались и не стали этого применять, потому что, несмотря на вот ускорение, что оно там в правильном тайме будет хорошо работать, это нереально потом поддерживать. Понятно. Ну, прикольный, кстати, эксперимент. И прикольно то, что у вас это получилось. Вот. Но получилось это как раз только благодаря тому, что есть стопроцентное покрытие тестами и мутиционка, и покрытие типами, и всё вот это. Если бы этого не было, ну что-нибудь, да, развалилось бы. Кстати, хочу сказать, что это ещё классно работает в декларативщине. То есть, например, берём какой-нибудь Джинкс, берём какой-нибудь докер или берём workкфлоу Гитхаба. О боже, я ему просто закидываю, говорю: "Слушай, у меня сборка идёт там столько-то, что надо сделать, чтобы оно ускорилось". И, естественно, особенно учитывая, что если МCшка есть, он сходил посмотрел, они дуже: "Так, смотри, значит, кэш надо заменить на там у Гитхаба свой, там вместо твоего, там создать тра-та-та". Я такой: "Ёлки-палки, вот это, конечно, красота". И тут тест, я сегодня это делал для Gitlabci C. И, короче, да, сборка-то оно ускорило, только работать перестала. А у меня нет, сработало. Ну, тут вопрос в том, что смысле деплой. Деплой-то сработал, но когда уже оно раздеплоивается, начинают сыпаться алёрты там, вот это всё. А, что-то сломало по что. У меня он просто поменял место хранения этого. М, короче, кэш там для пакета включил, потому что оказывается он неправильно был настроен. И второе просто кэш собственного образа. Так что там А у меня он поправил, короче, у меня была претензия к скорости деплоя, и оно у меня снизило тайминги на проверку хлсчеков. За счёт чего плой, конечно, беше ускорился. Не, не отключилось, снизило. Снизило прилично. И как раз вот в это вот за это окно немножко вышел мой фейл. И оно решило, что контейнеры лси вот они типа запустились. Смотри, возвращаясь к нашей теме протесты, а хочется вот ещё что сказать. В принципе, я об этом как-то даже на докладе рассказывал про отношения кента Бека, потому что его любят приводить в пример как человека там вот по поводу тестов. И сейчас ещё заранее посмотрел. Э, у нас, конечно, не было столько времени подготовиться, чтобы я прямо вам тут цитатами сыпал и говорил, кто когда в какой статье это сказал. Но если попросить, э-э, за 20 лет чат GPT рассказать изменения отношения к интеграционным и, э, юнит-тестам, то это не моя придумка. Это действительно такая общая история, которая там в определённые года начала трансформироваться. То есть вот это движение в сторону больше интеграционных. Но опять же, как мы сегодня выяснили, часто видишь, что что под чем ещё понимать. И там было несколько историй. Например, одна из историй была в том, что, ну, смотрите, мы в микросервисный мир пришли. Вообще какая нахер разница? Он же микро, он маленький. Какая нахера разница? Как он там внутри написан? Его переписать 2 секунды, тем более с, да, чатом GPT, поэтому нам гораздо важнее интеграция. И поэтому появился потом Трофи вот этот, какие-то разные там переосмысления. В конечном итоге как-то Кэнтебек, по-моему, это было ещё, прикинь, он же вот свою книжку написал, чёр знает когда. Его, по-моему, в 2015 или даже в пятом году спросили. Я вот, ладно, не буду вас обманывать, но его давно очень спросили. Говорят: "А что вы думаете на тём молодой человек?" Он говорит: "Вы меня задрали, я не пытался придумать теорию всего и мне платят не за тесты". Я пишу, и он классную фразу сказал, я где-то цитату эту выдавал, что я пишу минимальное количество самых необходимых тестов для того, чтобы убедиться в самых главных сценариях. А это очень похоже на то, вот как бы которую историю я пропагандирую. Опять же мы говорим про прикладной код. Если ты SQlite пишешь, вы знаете, да, эту историю, что там соотношение тестов коду 1 к ты000, наверное, там что-то такое. Это совершенно другая история. Там как бы там госзаказы, там этот военно-промышленный комплекс, там всё по-серьёзки. Это это не относится к тому, о чём мы говорим. А если мы говорим про вот обычные такие прикладные истории, опять же даже не банки, то там, конечно, с этим попроще. Короче, есть общая вот эта тенденция к тому, что юниты всё-таки в первую очередь для частей, которые сложное состояние, в которых много разных кейсов. При этом, естественно, там размер имеет значение. Ну и взаимодействие с внешними системами. Как-то так. В целом мне показалось, что у нас, э, видишь, если опять же с учётом терминологической вот этой немножко путаницы, не так уж большие, не такие уж большие различия в том, как мы с тобой себе это видим. А есть там парочку каких-то моментов в отношении того, что ты стараешься всё-таки изолироваться там от внутренней части. Я как бы себе это допускаю. В целом, я не думаю, что это вот если бы представить себе мой же проект, написанный вот так, как ты предлагаешь и как, в принципе, мы иногда тоже делаем на самом деле. Ну я бы не сказал, что это на что-то кардинально повлияло с моей точки зрения. Кардинально не знаю, вряд ли. Да. А в плане именно м тестирования там ваших кишков фреймворка, ну то же самое. Единственное, знаешь, я что, наверное, хотел спросить. Вот смотри, если такой интересный момент, допустим, ты пишешь систему плагинов, она у вас 100% есть. То есть типа хуки, которые отрабатывают там на какие-то истории. Угу. Я вижу всегда привожу этот пример, потому что прикольная штука с точки зрения того, что её можно тестировать двумя способами. Первый способ: ты можешь именно прямо всё промокать для того, чтобы прямо проверить, что вот этот хук там отработал. Либо ты можешь это делать косвенно, ты просто передаёшь туда какой-то плагин, который тебе позволяет увидеть, что он был запущен как надо, когда надо, и он, соответственно, внутренний стоит поменял. Вот ты как и это позволяет тебе не лезть внутрь системы, это тебе позволяет как бы как будто бы снаружи это протестировать. Вот ты какой обычно эту штуку называют мок и спай. То есть мог - это вот когда ты во внутреннее состояние полеса там смотришь всякие штуки. А спай - это когда у тебя специально подготовленный вот этот вот плагин, который ну у которого часть его публичного апе - это посмотреть, что у него внутри. То есть который записывает, что с ним случилось. Я за второй подход, потому что я не люблю. Да, я за спай, потому что, а, это внешний контракт, на самом деле, по отношению к системе плагинов. Это внешний контракт. То, что, э, как раз наши внешние пользователи у фреймворка получат в итоге и то, как их плагины будет менять наша система. То есть вот это она не должна ломать, это публичный контракт. То, как она внутри это делает, какие там у неё состояния, что она там вызовет при этом, это её дело. Если туда она промечена как там Final private, то, блин, ну не лезьте туда. Вы себе просто заливаете цементом реализацию, а не контракта. Так делать не надо. Я, кстати, с тобой вообще на 100% согласен. Знаешь про старый добрый паттерн, который называется Паблик Морозов? Да. Офигенный. Вот, ребят, если кто если кто вдруг не знает, просто это старыйстарый добрый паттерн, который использовался для тестирования кишков. Он работает так. Если у вас есть протект методы, то их наследнике можно сделать паблик. Соответственно, что делали люди? Они просто наследовали, сделали все методы пабликами и тестировали. Вот поэто название просто идеальное. Да. Паблик Морозо. Да. Паблик Морозов. Вот PHP в Робе такой проблемы нет. Не нужен Паблик Морозов. Ну там можно напрямую, да, без особо можно и так влезть. Ну где рефлексии везде можно, да, но в некоторых языках реально с этим прямо сильно проще, да. Саш, тебе огромное спасибо, что ты пришёл. Мне кажется, мы много разобрали. Ребят, если вам понравилось, поставьте лайк. Не понравилось и не согласны, обязательно лепите дизлайк и пишите, а за что вы всё-таки топите больше, за то или за то? Где вы считаете, что мы оба не поняли терминологию и всё на самом деле просто. И как надо правильно это понимать, тоже обязательно напишите. Мы обязательно почитаем вместе со всеми посмеёмся. Ну ладно, я шучу. А всё равно напишите, будет интересно, да? Потому что для меня все эти слова, они всё-таки слова, но прикольно про это рассуждать, потому что это всегда позволяет, видите, раскрыться каким-то новым деталям. Сегодня, мне кажется, мы много интересного раскрыли для тех, кто никогда с таким не сталкивался. Саш, тебе ещё раз спасибо. Спасибо всем. Всем пока. До новых встреч. เฮ

Never lose your place, on any device

Create a free account to sync, back up, and get personal recommendations.