Тимофей @matim_ioioi
Frontend engineer
Information
- Rating
- 5,353-rd
- Location
- Россия
- Date of birth
- Registered
- Activity
Specialization
Frontend Developer
Lead
From 300,000 ₽
JavaScript
TypeScript
Node.js
Vue.js
Webpack
Jest
SCSS
HTML
GraphQL
Kubernetes
Frontend engineer
Вы запускаете его в сетап-файле (setupFiles). Сетап-файлы запускаются каждый раз на каждый тест-файл, а не перед всеми тестами. Это как и в джесте, так и в витесте работает
Ну вот не совсем уж так. Такая категоричная изоляция нецелесообразна. То, что покрыто типизацией — можно не тестировать (я бы даже сказал «не нужно тестировать»). Про пинью — это тоже норм, это по сути просто подготовка фикстур для ваших тестов (например, как засидить какие-то данные для тестов в бд)
В двух словах это полностью не опишешь, а слишком много писать, простите, лень :)
Сколько время выполнения тестов? - много. Очень много. Поэтому я это всё и написал. К сожалению, не можем получить столько ресурсов, чтобы это всё отрефакторить и оптимизировать, так как это дорого. Сейчас около 3 тысяч тестов. Это на все проекты (у нас много модулей). Много из них мусорных, много из них неправильно написанных. Какие-то из них на джесте, какие-то — на витесте (в процессе переезда на витест). Если суммарно считать время их выполнения (считаем на ci, а поэтому оно ещё больше), то будет примерно минут 20 (да, локально это может превратиться в минут 10, а то и меньше, но нам важен ci этап).
Лишний случайный коммент :)
Независимая от бекенда разработка и написание тестов почти никак между собой не связаны. Msw используется в development-режиме только для удобства разработки (либо пока бекенд не готов, но есть контракт, либо даже если бекенд готов, но просто надо подставлять определённые наборы данных, например, чтоб не идти по реальным юзкейсам и не заводить что-то там у своего пользака). Завязывать тесты на моки msw, как по мне, идея сомнительная.
Попробую ещё раз объяснить, почему же:
В рамках теста не мокается внешнее API. В рамках теста выполняется реальный запрос. А вот этот реальный запрос ловит «локальный сервер». Это значит, что для запуска тестов необходимо поднять тестовый сервер, который имеет необходимые замоканные роуты. Да и зачем для каждого теста поднимать весь этот сервер со всеми моками роутов (а исходя из статьи вы запускаете сервер именно в сетап-файле, который выполняется перед запуском каждого тест-файла)..? Не имеете ли вы случайно проблем со времени выполнения тестов? Тут ещё, конечно, важно было бы подметить, какое количество тестов у вас имеется (так как если их условно 200, то, конечно, вы особо не заметите просадок из-за этого). Из этого всего пункта имеем: повышенное потребление ресурсов и увеличение времени выполнения тестов. А этого нам уж точно не хочется видеть, так как тесты уж явно хочется гонять не только локально, но ещё и на ci
Проблема неявности. Тут просто повторюсь, так как разжёвывать особо нечего: в ваших тестах при таком подходе не описана имплементация мока запросов, что усложняет координацию во время добавления/изменения/удаления/рефакторинга/любойдругоймодификации тестов и повышает связанность — тест зависит от вендора (в данном случае msw) и, более того, от реализации обработчика запросов в конкретном обработчике msw (как будто бы не очень хотелось получить связанность между msw, который используется для dev-режима и тестами, не так ли?)
А вот по этому поводу я как раз и писал про переиспользование фикстур между реализацией msw-обработчиков и тестами. Либо вы не дочитали, либо не так поняли :)
А вот тут, извините, но совсем не понял этот поинт. Каким образом ваш подход овер мока запроса в тесте позволяет тесту стать «чище»? Ведь даже при вашем подходе, если проверять несколько юзкейсов, так или иначе вы будете описывать имплементацию мока в тесте (ну да, в вашем примере вы просто опишете ответ для эндпоинта сервера, но разницы между этим абсолютно никакой со стороны количества кода)
Подчеркну: я не пытаюсь вас переубедить не делать так, как вы делаете. В каждой системе свои правила и удобства выстраиваются в зависимости от тех, кто над этой системой работает. У меня посыл такой: если вы видите решения описанных мной проблем (или не видите проблем в этом вовсе) — настаиваю на том, чтобы вы конструктивно описали это (либо решения, либо почему это не является проблемами). И, да, тут «конструктивно» будет даже с точки зрения вашей системы, что может не совпадать со всеми остальными. И это не будет означать, что у вас (или у нас) что-то не так, просто к каждой системе свой подход :)
Надеюсь, я правильно донёс свои мысли. Спасибо за ответы!
Спасибо за статью! Но так и не понял, в чём плюс использования msw в тестах :)
В Вашем примере тест зависит от написанного хендлера в msw, о котором из теста явно не известно вообще ничего
Да и к тому же, зачем поднимать «сервер» для тестов
Моё мнение: каждый тест должен быть самодостаточным. Это значит, что в каждом тесте должен быть мок запроса (конкретно реализация мока зависит от того, что вы используете, будь то fetch, axios или какой-нибудь gql-клиент). Ну или в каждой группе (дескрайбе), если тест-кейсов несколько на один и тот же набор данных
В общем так: есть фикстуры для тестов (наборы различных данных или функции для генерации нужных наборов данных); есть тест, в котором используются эти фикстуры в имплементации мока запроса; если практикуете параллельное программирование, то можно использовать ранее подготовленные для тестов фикстуры уже в хендлерах msw (ну или наоборот, если вы сначала реализуете компонент с моками на msw, а потом уже пишете тесты)
А вот сам по себе msw хорош, особенно для тех, кто практикует tdd и контрактное программирование (tdd тут не потому, что в тестах надо использовать msw, а потому, что на этапе подготовки тестов уже будут подготовлены фикстуры, которые можно использовать в хендлерах msw)
А вы знаете спецификацию наизусть?
Это просто очередная хорошая статья (своим наполнением), причём даже с ссылками на спеку. Поведение является «странным», как раз-таки, пока не прочитаешь спецификацию. Что статья и подчёркивает.
Или для таких как вы обязательно было в названии писать «…странностей (которые перестают быть странностями при прочтении спецификации) css…»? ;)
Или вы считаете, что каждый разработчик перед тем, как разрабатывать коммерческий продукт должен пойти и вызубрить спецификации всех нужных ему технологий? :)
Возможно
Честно говоря, не знаю, как по правилам хабра (я пока не писал тут статей, поэтому не изучал этот вопрос), просто выразил своё мнение по «среднему по больнице» относительно кучи других мной прочитанных статей. Видимо, тут каждый выставляет как хочет :)
Тогда, как я и написал, всё встаёт на свои места :)
Ну, никто не говорит прям брать код из продакшна проекта под NDA и сувать его сюда. Можно взять юзкейс из него и описать пример со всеми вытекающими. Да, и, на самом деле, Вы бы в любом случае полностью весь код с бизнес-логикой и тд не вставляли, а вставили части, которые относятся непосредственно к сути данной статьи. NDA может быть, конечно, всякий, но, думаю, никто бы не был против этого, если уж так говорить :)
Думаю, многим было бы интересно узнать, в каких именно реальных кейсах это можно использовать с пользой, а не во вред. Но это Ваше право, как автора статьи
Спасибо за ответ!
А где написано, что он не рекомендуется? Просто о нём автор нигде ничего не нашёл в «интернетах»
Иногда (точнее, почти всегда), для DX, а так же, что не маловажно, для оптимизации, лучше разграничить такие компоненты на разные, а не держать все варианты представления в одном компоненте. Это улучшит читаемость кода, его дальнейшние модификации (при надобности) и уберёт лишние пропы (или просто условия внутри компонента) по которым Вы хотите отобразить то или иное его представление
А в чём, собственно, проблема заключается? Само направление статьи казалось интересным, пока я внезапно не наткнулся на её конец.
Проблема не описана
Пример посредственный. Объясню: то, что можно сделать композабл, который принимает в себя пропы и эмиты какого-либо компонента, использует эти пропы и эмитит эти эмиты и ежу понятно
Не очень понятно, в каких реальных кейсах это может использоваться, так как даже при «однотипной» логики событий (как пример, возьмём различные типы интерактивных элементов формы — текстовый инпут, численный инпут, датапикер и прочие, где есть один и тот же эмит «изменения текущего значения») — логика внутри абсолютно разная, а вот зарывать обработки всех разных типов в один «общий» композабл — это оверхед, так как проще (и для чтения кода, и для рантайма, и для будущих модификаций кода, и для последующего рефакторинга при надобности) обособить ответственность конкретного компонента именно в нём
Но, если уж представить, что нам прям хочется сделать что-то такое универсальное, то использование эмита в описанном примере не совсем корректное. Если уж Вы решаете проблему «переиспользования одинаково названных и по сути дублирующих логику эмитов», то недостаточно описать тип эмита в самом композабле — как минимум понадобится дженерик, т.к. такие эмиты могут «выбрасывать» разные типы данных, как максимум — всё-таки придётся дать возможность прокидывать свой обработчик на данное событие, так как логика в каком-то компоненте может быть нетривиальна, а нам же захочется его поместить в эту нами созданную «универсальную бутылку» (не, ну а зачем тогда мы это делали, верно же?). Но тут опять же поинт про то, что это усложнение кодовой базы, которое в будущем вряд ли даст нам профит — скорее наоборот, принесёт проблем, когда нужно будет что-то изменить в одном из типов компонентов (либо придётся обработку выносить на верхний уровень — в сам компонент (но зачем мы тогда всё это делали, опять же?), либо изменять реализацию уже в самом композабле — что будет довольно проблематично, так как его уже будут использовать несколько компонентов). В итоге получаем некий «вендор лок», который станет блокером для некоторых задач и потребует ресурсов на устранение созданных нами проблем в рамках всеми любимого «техдолга»
Возможно, в чём-то я не прав, что-то не понял из Вашей статьи и так далее и тому подобное. Основной мой поинт в чём: из статьи не понятна проблема, предложенное решение соответственно не понятно, какую проблему решает, так как проблема явно не описана, а пример уж очень поверхностный, что из него так же не становится ясна проблема
Если бы Вы добавили пример реального использования (да, прям реального использования из коммерческой разработки) — возможно, это бы всё расставило на свои места
В любом случае, спасибо за статью!
UPD: если Вы имели ввиду компоненты, которые действительно делают прям одно и то же, но просто имеют разное представление (да, да, такое действительно бывает) — в таком случае всё встаёт на свои места. Кроме одного: почему у статьи уровень «сложный»?)
Чтобы такие советы не были «вредными», нужно для себя, своих конкретных целей и выработанных для себя лучших практик вычленять из статей именно то, что нужно именно Вам. А автор пишет лишь свой опыт и свои наблюдения. Если Вы так не делаете, а делаете по-другому, это не значит, что то, что написал автор — неверный подход. У каждого подхода есть плюсы и минусы
А где? Прочитал, но, что-то не увидел: а) ничего более полезного, чем в этой статье кроме, как всегда от Вас, поливания грязью всего, что не «мол», б) где написано, что так не надо делать и почему? :)
Да нет, заморачиваться не придётся, это всё легко делается, вот только использовать разный билд для разных окружений — это плохо, но щас не будем копать эту тему, у меня и был вопрос в том, в режиме разработки запускаются тесты или проходят на стенде. Ответ я получил, спасибо)
Не может быть на странице два одинаковых айдишника, т.к. даже если страница одна, то блоки (или контексты) будут разные :)
Спасибо за ответ!
Спасибо за статью! Хорошее направление материала. Ранее не видел подобных статей (не искал, конечно, но и не попадались)
Есть несколько вопросов и предложений из личного опыта
Вопросы:
1. При рандомизации селекторов Вашим предложенным «лучшим» способом через хэши не понятно, как команда автотестирования будет внедрять их в свои тесты?
2. Про скрытие атрибутов — на стенд выкладывается прод сборка приложения. Соответственно, такой подход с «удалением» атрибутов из бандла прод сборки не поможет (я про тот случай, когда e2e тесты запускаются не напрямую «в приложении» (например, на этапе ci), а отдельными инструментами. P.S. не претендую на инстанцию истины, но у меня был опыт, когда e2e и нагрузочные (они тут не при чём, но просто к слову) тесты гоняли непосредственно на стенде с помощью гатлинга). Могли бы Вы раскрыть тему в таком кейсе? Или же Вы только про тот кейс, когда тесты запускаются «в приложении» (опять же, например, на этапе ci)?
Предложения:
1. Вместо построения айдишников с помощью хэшэй мы использовали просто «префиксы» для них. Например, от названия страницы, конкретного блока или какого-то определённого юзкейса и добавляли к нему индекс итема (примерно так: «some-page:region-list[0]»)
2. Для упрощения работы с автотестировщиками можно сделать некий «контракт» по названиям тестовых айдишников. Например, будем называть так: <страница>:<тип блока>:<наименование блока/контекст>[индекс, если это элемент списка], то есть для страницы каталога, фильтра по регионам будем использовать такой вид: «catalog:filter:regions», а для элементов списка региона «catalog:filter:region[0/1/2/3/…]». Где «тип блока» — это заданный перечень типов, например: просто блок (будь какой-то див с инфой) — block, кнопка — button, фильтр — filter (если нужно разбиение по типу фильтра, например, инпут или селект, можно сделать input или select соответственно) и т.д. и т.п.
В целом, статья имеет место быть. Разве что, есть несколько нюансов
По пунктам:
3 пункт из производительности: да, ClientOnly можно использовать как компонент для избежания проблем гидратации, в том случае, когда компонент ведёт себя по-разному на клиенте и сервере. Вот только загвоздка в Вашем примере. Объясню: кейсов, когда компонент ведёт себя по-разному на клиенте и сервере, ничтожно мало и большинство из них просто ошибка разработчика. В Вашем примере используется isMobile, который по своей логике проверяет, с какого устройства заходит клиент. Это можно сделать и на сервере (например при помощи user-agent). Тут тоже есть нюанс, т.к. определение устройства с помощью user-agent не является точным. Вообще, в идеале вёрстка должна быто одинакова и разделяться на мобильную/десктопную/какуювамнадо через медиа запросы в css, но, такое, к сожалению, тоже не всегда возможно. В общем, пример довольно спорный и не соответствует, так скажем, «лучшим практикам», но, в целом, жизнеспособен
2 пункт из безопасности: опять же, из примера — проверка «на админа» должна корректно производиться и на сервере, так что бот этого и без ClientOnly не увидит, т.к. директива просто не даст этому блоку «создаться». Единственное, в чём может быть суть примера — если проверка «на админа» довольно затратна и её хочется «убрать» из серверной обработки. Но и в этом случае ClientOnly не понадобится, т.к. переменная, которая в себе содержит флаг, админ это или нет — изначально должна быть false и изменяться на true уже на клиенте
Все пункты из «реальных кейсов использования» так же не совсем корректны. Чаще всего такие компоненты обособлены (как и у Вас в примере). Если сделать обернуть рут в ClientOnly, то весь setup всё равно будет выполнен на сервере (да, за исключением клиент-хуков, понятно дело), что тоже может повлечь какие-либо накладные расходы на стороне сервера. Так что внутри компонента особо нет смысла оборачивать рут в ClientOnly (даже если компонент везде должен использоваться только на стороне клиента) — оборачивать в ClientOnly нужно сам компонент по месту его использования
В общем, суть проста: если статья для «новичков», то и примеры должны быть корректны и сопоставимы с реальностью. Ведь многие, читая примеры в казалось бы «хорошей статье», используют данные в ней примеры «напрямую» без раздумий, к сожалению
А, знаете, я тут взвесил все «за» и «против», и, пожалуй, соглашусь с Вами
Геморроя он действительно много доставляет
Но, всё же довольно много компаний его используют, на сколько я знаю (хотя это не противоречит тому, что, возможно, на ресте у них было бы всё ещё лучше)
У GraphQL есть свои плюсы. Да, есть и минусы, без этого никуда. В каких-то случаях GraphQL будет лучше, в каких-то REST
Подходите с умом, а не просто кидайтесь грязью в инструменты, которые, видимо, не поняли, зачем нужны ;)
Статья — отличный байт на комменты, браво
Что касается отличий в написании — для этого существуют статические анализаторы. А они ещё и автофиксить могут.. Поэтому в рамках команды/проекта/системы/чегохотитедругого составляется стайлгайд, пишутся для него автоматизации и внедряется в проекты. А потом 25 человек, которые «пишут по-разному», оказывается, что пишут одинаково. Заходишь в любой кусок кода и заранее знаешь, что и где искать. А для нетривиальных ситуаций существуют комментарии. Их, вроде, никто не отменял
На любом ЯП можно написать такой код, чтобы его никто кроме автора не понял. Смотря какая цель :)
Да всё оно подходит, если применять так, как действительно нужно
Условный пример: есть у вас компонент. Пусть будет “stepper”. На вход ему приходят массив элементов и, допустим, индекс активного элемента. Индекс активного элемента можно не передавать, в таком случае, по дефолту активный элемент — первый. Есть кнопки «вперёд» и «назад». Если индекс активного передан — при нажатии на соответствующую кнопку будем кидать событие наверх, что индекс меняется с такого-то на такой-то. Если не передан — будем использовать локальную переменную. У активного элемента есть свои плюшки — допустим, какой-то класс, который красит текст этого элемента в другой цвет (это уже сам UI). И т.д. и т.п.
Неужели этот компонент не нуждается в тестировании? Или, хотите сказать «ну то, что ты описал, это ведь логика, а не представление»? И, да, будете правы. Но представление компонента без его логики это ничто (конкретно про приведённый мой пример). Нет смысла тестировать отдельно представление (условно, отрендерили с заранее заданным состоянием, проверили, что все классы на месте и всё). Здесь тест будет иметь смысл только если тестирование будет затрагивать и логику, и представление. Ну и конечно же, юнит-теста недостаточно будет для этого компонента. Так же понадобится интеграционный, чтобы проверить случай с переданным активным индексом, работой событий и изменением этого индекса, а в последствии и изменением представления этого компонента. PS пример лишь условный, первое, что в голову пришло. Думаю, можно было выдумать что-то более подходящее, но не суть
Но частично суть передана в правильном направлении — покрытие, как таковое, не отвечает ни за качество, ни за работоспособность протестированного кода. Всегда и везде будет баланс. И только с балансом есть понимание, что можно и нужно использовать различные инструменты и подходы. У каждого из них своя цель