Comments 30
Знать xpath все равно стоит для случаев, когда:
Локатора ещё нет, а тесты нужны
Локаторы есть, но у вас имеется структура элементов вида data-testid=my-list: data-testid=my-item, (где у каждого элемента в списке может быть ещё и data-testid=my-title, и вот по нему и надо найти)
Лучше, если сначала фронтенд будет готов для тестов, чем писать тесты и затем исправлять старые локаторы на новые…
Если у каждого элемента внутри вложенности есть свой тайтл и айтем, это исправляется со стороны фронтенда, в тестовый локатор передается id элемента, и для каждого элемента локатор продолжает быть уникальным.
Конечно лучше быть здоровым и богатым, чем больным и бедным (с), но ждать "готовности" фронта можно долго, т.к. такие доработки обычно не в приоритете, но основная цель тестов -- помогать в обнаружении ошибок/экономить время на регресс, а не быть написанными на века, поэтому оптимальнее их все-таки создать раньше и на том, что есть, а потом обновить локаторы, все-таки их правка занимает меньше 5% от времени на разработку тестов.
Или я не так объяснил, или не так был понят, давайте на примере (искусственном, поэтому без вопросов к логике)): у вас есть страница покупок, где есть два списка: "купить" и "куплено", вам нужно проверить, что итем переехал из одного списка в другой, поэтому просто сверки по тайтлу будет мало, нужно еще учитывать родителя, тут одним лишь test-id не обойтись.
Проверить появление нового аттрибута у айтема можно силами фреймворка через hasAttribute()/toHaveAttribute(), а ещё, купленный айтем может имет совершенно другой локатор data-testid-bought(itemId)..
просмотрел статью по диаганали, по озвученным проблемам уже давно существует решение в виде page object, отсюда в тестах не используется xpath, а используется page object в котором уже скрыта (инкапсулирована) работа с xpath.
Итог: не обязательно добавлять data-testid можно использовать обычные id, class, tag если они есть, вопрос именования это отдельная тема, не будем спорить нужен ли БЭМ или альтернативы.
Добавляя data-testid мы не перестаем использовать xpath, мы вместо использование классов, тэгов, id. Начинаем использовать пользовательские атрибуты.
Куда бы ни был инкапсулирован локатор, тесты все равно упадут, если произойдут озвученные риски (смена верстки итд, но и с "data-testid" оно может упасть при кардинальных изменениях, если уж на то пошло).
так кто вам гарантирует что при изменение верстки тот кто верстал не посносил data-testid ?
тесты на то и тесты чтобы не заливать то что не проходит тесты, поэтому тот кто поменяет верстку, будет обновлять и page object, тесты ему напомнят что упало и на что обратить внимания, для этого они и пишутся.
например пусть была некая колонка с балансом в профиле пользователя, а после редизайна она перехала куда то в другое место, на другую страницу (мой баланс например и там текущий баланс и операции пополнения и расходов) как вы ее будете находить не меняя тестов ? будете оставлять колонку на баланс в профиле с display: none ? (костылище)
как в этом случае мне поможет подход озвученный вами ?
тоесть при подобном кейсе в моем варианте.
Разработчик обновляет верстку (редизайн и тд) сфокусировавшись на этом.
Запускает тесты чтобы заметить регресс.
Исправляет регресс у упавших тестов.
MR
В вашем случае на первом этапе помимо фокусирования на задаче, разработчик еще будет смотреть data-* атрибуты и где они используются в тестах, получится он на первом шаге будет делать еще и второй и третий этапы.
где тут отказ от xpath/css селекторов ?
ваше предложение сводится к тому что используйте пользовательские атрибуты в xpath/css селекторах в тестах вместо class/id/tag.
Вот я не согласен на "вместо" я считаю что надо использовать там где возможно стандартные селекторы основанные на tag/class/id, а где необходимо можно добавлять и пользовательские атрибуты, но не строить тестирование только вокруг пользовательских атрибутов.
Чем предложенный вами data-testid отличается от обычного id ?
Почему человеку надо вместо id использовать data-testid ?
причем если посмотреть описание id http://htmlbook.ru/css/selector/id
Идентификатор (называемый также «ID селектор») определяет уникальное имя элемента, которое используется для изменения его стиля и обращения к нему через скрипты.
я даже выделил важное в цитате, как раз то как вы собираетесь использовать его.
как то я слишком расплывчато написал, попробую кратко по пунктом сумировать
вместо data-testid можно, а возможно даже лучше использовать стандартный id
пользовательские атрибуты (data-*) нужно использовать по назначению там где нет стандартных.
для того чтобы изолировать тесты от структуры документа и не использовать селекторы в тестах можно использовать page object, но это надо далеко не всем
использование пользовательских атрибутов это не значит что вы отказались от css селекторов.
В вашем случае на первом этапе помимо фокусирования на задаче, разработчик еще будет смотреть data-* атрибуты и где они используются в тестах, получится он на первом шаге будет делать еще и второй и третий этапы
Совсем не уверен, что разработчику нужно смотреть как эти локаторы используются в тестах. Если разработчик видит этот локатор, значит будет держать в уме, что в тестах этот локатор используется, это и помешает ему посносить. Во всяком случае, в моей практике такого не было. Продублировать не получится, подскажет умная IDE…
ID, так же, ка к и всё остальное — может в любой момент измениться, причем разработчик спокойно может в любой момент это изменить, без согласования, при этом тесты не сломаются, поскольку не завязаны на классы, теги и обычные айдишники.
Page object - хороший, годный слой абстракции. Сделал такой вывод, почитав по диагонали его концепцию в доках Селениума.
Он не решает озвученную проблему. Да, вы переносите описание локатора непосредственно из теста в какое-то общее место (описание структуры документа), отделяя его от ассертов, но вам всё ещё нужно учитывать нюансы выбранного способа локации.
Предлагаете доступ к элементам по атрибуту id? Прекрасно. Только разрабу всё равно придётся эти айдишники проставлять для вашего удобства. А потом в проде вас будут нещадно парсить, блочить рекламу и прочее.
Почему бы не договориться, что в тестовом (предпродовом, если угодно) окружении, где проходят наши тесты, будут использоваться нужные юзер-атрибуты? Да, это нагрузка на разраба, ему нужно будет сверяться с таблицей (которую должен составлять тестировщик, тут отдельная тема того, что эту таблицу нельзя прописать в код page object, но можно описать в документации) и вручную прописывать нужные атрибуты после правки кода и перед пушем. Но тут мы не трогаем id, которые используются в коде приложения и могут быть рандомными. Перед пушем в прод достаточно прогнать скрипт, который убирает из кода добавление юзер-атрибутов.
Да, то же самое можно с делать с id. И в зависимости от окружения выдавать разные значения этого атрибута (статик в тест, динамик в прод). Я прикинул, в этом просто больше кодинга, а нагрузка на дева такая же (или больше, кодинга же больше).
> 4
Да, но глобально - это вопрос унификации взаимодействия. Тестировщика с разработчкиом, тестов с кодом, пользователя с продуктом. Как я написал в каменте ниже, нужно ориентироваться на представление пользователя. И page object, и юзер-атрибуты, и локаторы на основе ARIA вместо селекторов - инструменты, которые мы можем применять в конкретном проекте
В любом случае, это всё не панацея для выстраивания процесса тестирования. Это может в той или иной степени помочь, и, в зависимости от проекта, быть применимо или нет.
Кстати говоря, расставлять локаторы не обязательно разработчику, возможно стоит один раз показать, а дальше просто подключаться на ревью простых задачек на добавление этих локаторов.
В дальнейшим эти локаторы попадут в PageObject и будут использоваться в тестах.
Решение с тестовыми аттрибутами используется, причем многими компаниями, мой поинт был отказаться от построения XPath/CSS путей, завязанных на сущности, которые меняются.
так кто вам гарантирует что при изменение верстки тот кто верстал не посносил data-testid
я специально вроде в скобках написал, что не всегда есть гарантия, но некоторую защищенность в этом плане может обеспечить, например, использование библиотеки компонентов, где data-testid прописан именно для компонентов, а не только в верстке.
Чем предложенный вами data-testid отличается от обычного id
лично я ничего не предлагал, но у меня есть ответ: в некоторых популярных фронтенд-фреймворках id может быть автогенерируемым (возможно, чтобы избежать использования кеша для старых стилей, но это не точно)
PS: Глядя на комментарии под статьей, хочу акцентировать, что нигде не писал такие фразы, как "нужно так делать" и "всем 100% подойдет этот подход" :)
А в чём смысл писать "/html/body/div[1]/p/a" или "//a[contains(text(),"example link")]"
вместо "//a"?
потому что на веб-страницах обычно больше одной ссылки и их надо как-то различать?
И как их различает "body > div > a"?
Если мы не в вакууме, то тут уже много вариантов: по положению(путь/индекс), по атрибутам, по значению. Хотите как в CSS //body/div/a или
? Просто тут явно хотели некрасивый и нефункциональный X-Path. Это обесценивает всю статью.
Это не обесценивает всю статью, просто примеры как для XPath, так и для CSS-Selectors нерелевантные.
В этом и проблема — много вариантов. Можно написать хорошо, можно не очень, а можно вообще не использовать язык для обращения к локатору, а обращаться однозначно, зная что он уникален, если позволяет фреймворк.
Спасибо что обратили внимание на data-аттрибуты. Это часть стандарта и очень мощный инструмент.
https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
Я не вижу никаких аргументов против введения таких аттрибутов в код фронтенда.
Это часть "testability". И висеть в проде эти аттибуты тоже не обязательно должны. Можно заставить компилятор или препроцессор убрать эти аттрибуты при сборке.
Да, придется вложиться в это. Но это и есть серьезное отношение к автоматизации тестирования. Бывает, конечно, что тестируемость случайно возможна, посредством каких нибудь манипуляций, или из-за свойств системы, но полагаться на это не стоит. Тестируемость это инвестиция, инвестиция в будущее сложной системы. И она стоит денег.
А автоматизация тестирования, это разработка программного обеспечения. И не нужно делать вид что "ну че там пару скриптиков написать чтобы кнопочки потыкать". Чем дольше притворяться, что автоматизация тестирования это не "программирование систем", тем дольше она будет у вас непрофессиональной и нездоровой.
Часто такое мировоззрение в компаниях в которых DevOps - это "чувак который настраивает Jenkins".
В Selenium из коробки отсутствует поддержка тест-аттрибутов
Судя по вашим примерам в таблице, основанным на common CSS-селекторах, она отсутствует везде, кроме Playwright.
Получается, мы просто используем соглашение между фронтом и QA для удобной автоматизации тестирования. Есть серьезный недостаток, который вы не затронули, он касается излишнего автоматизма автоматизации (простите за тавтологию): на фронте может быть поломан UX, но при этом тесты будут проходить. Нам так или иначе нужно отталкиваться от представления пользователя, от того, как он взаимодействует с приложением (страницей, документом).
В том же Playwright есть локатор page.getByRole(), который призван если не решить, то помочь в решении с представлением пользователя. Само собой, это можно и нужно комбинировать с использованием тестовых атрибутов, если есть такая возможность.
Плохо представляю, каким образом тесты будут проходить, если поломан UX…
Если тест написан правильно и в нём есть ассерты, он не сможет пройти..
getByRole(), кажется, создан для других задач, но через него действительно можно обращаться к аттрибутам, но вопрос - зачем
await page.getByRole('checkbox', { name: 'Subscribe' }).check();
Если тест написан правильно и в нём есть ассерты, он не сможет пройти..
Сможет. В зависимости от вашего "правильно". Вы заполняете форму, получаете нужный ответ. А пользователь не может. Потому что разраб добавил шифрование пароля, например, поменяв id элементов (используя финты с динамической скрытой формой). Но где-то прошляпил с уровнями иерархии. В итоге ваш тест работает, а в проде не работает.
кажется, создан для других задач
Для тех самых, чтобы абстрагироваться от структуры DOM и его атрибутов.
через него действительно можно обращаться к аттрибутам
Ну нет же, вы выбираете элемент/набор элементов по ARIA (то, что доступно пользователю), а уже после проверяете атрибуты с помощью фильтров. Где-то там оно должно упасть в случае косяка разраба.
Вашу проблему подсветит результат сценарного е2е теста: "успешная авторизация и переход в личный кабинет (например)". Здесь я имею в виду не переход в личный кабинет через ссылку или параметр next в query, а как результат абстрактной, успешной авторизации. В тесте проверяется весь путь, дополнительно можно проверить простановку необходимых кук.
Если честно, не понял, как то, что вы подсветили связано с аттрибутами, ну ладно.
getByRole создан для поиска аттрибутов, в том числе созданных неявно. Это решение может подойти для тестовых локаторов с названиями, которые не поддерживаются getByTestId() или если getByTestId() отсутствует в фреймворке.
Подход, который зачастую упирается в нежелание разработчиков писать тестовые атрибуты и нежелание менеджеров эту практику правильно прививать. Однако если взлетает, то жить становится и правда легче.
Добавлю только, что в случае с Селениумом:
driver.findElement(By.css('[data-testid="entry-btn"]')
Можно просто заэкстендить класс By, добавить туда метод testId():
public static By testId(String selector) {
if (selector == null) {
throw new IllegalArgumentException("Cannot find elements when locator is null");
} else {
return new ByCssSelector(String.format("[data-testid='%s']", selector));
}
}
И после этого вполне себе использовать радостно свои тест-айдишки, импортируя свой соответствующий By, вместо селениумовского:
driver.findElement(By.testId("entry-btn"))
Совершенно очевидно, что тесты должны быть привязаны именно к тому, к чему привязан внешний вид/поведение фронтенда. Если у вас обычная верстка по БЭМ и сырой JS типа
document.querySelectorAll('.button__delete-all-users').addEventListener(...)
то любые варианты будут хуже селекторов классов. Если же, как в вашем примере, Реакт и material-ui, и вы, вообще говоря, не можете гарантировать какой DOM у вас будет на выходе, то тогда, конечно, дата-атрибуты выгядят неплохим вариантом. Только это костыль, который приходится делать за неимением лучшего.
С основным посылом я согласен, приложение должно быть тестируемо и о том как оно будет тестироваться надо думать еще на этапе требований.
Но для большинства проектов начиная со средней сложности подход не эффективен.
Я уже молчу что далеко не все используют в первой итерации приложения фронтенд фреймворк облегчающий жизнь автотестерам. Банально не редко просто нет возможности прописывать ID везде.
Xpath/Css маст хев для джуна, иначе он сможет работать на 1 проекте где такое срабатывает из (условно) 10000.
строить локаторы, используя все возможности XPath/СSS - на основе названий классов, вложенности, и всего, что может в любой момент измениться — плохо
1. Надеюсь вы не говорите о чем-то вроде Full path, там нулевая надежность
2. Эти уникальные локаторы будут писать разработчики, а это время.
3. В DOM может быть один и тот же ID у нескольких элементов потому что разраб ошибся. Будем ждать когда этот супер лоу баг пофиксят. Xpath позволил бы за секунды это исправить.
4. Оказывается что сложный элемент вроде пункта меню может включать в себя кучу других и каждый несет бизнес информацию которую надо проверять/нажимать/искать/...
Оказывается что если разрабу библиотека и позволяет прописывать ID, то работа неожиданно увеличивается в разы.
Я бы сказал так, если есть проблема, что XPath/Css приходится очень часто редактировать, то должны быть причины и надо разобраться:
- Может фронт активно переписывается - тогда это норм
- Может тот кто пишет локаторы в автотестах не понимает как делать их надежными - надо научить
- Может действительно библиотека фронта не дает возможности писать надежно - посчитать траты времени=денег на поддержку локаторов и договариваться с бизнесом делать ID или использовать другую библиотеку фронта.
Расставлять локаторы может любой инженер в команде, не обязательно разработчик. Правка копеечная, но сэкономит время и улучшит стабильность. Достаточно один раз договориться внутри команды и внедрить этот подход, тогда разработчик фронтенда будет сразу, при реализации чего-то нового добавлять локаторы.
На мой взгляд - проще один раз попробовать, тем более этот подход уже применяется во многих компаниях.
статья должна называться так: "Как сделать вид, что отказался от локаторов, заюзав синтаксический сахар тестовых фреймворков"
Как отказаться от XPath/CSS локаторов в тестах