Мы используем Cypress для тестирования E2E и собираемся приступить к задаче перехода от селекторов тегов и имен классов к атрибутам данных, чтобы сделать селекторы менее хрупкими.
Мой вопрос касается использования атрибутов данных. Кипарис рекомендует использовать data-cy или data-test или data-testid.
Некоторые из самых сложных селекторов включают выбор строки и столбца из таблицы. Пример:
<!-- example of hard-to-test markup -->
<table class='users-table'>
<thead>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<thead>
<tbody>
<tr>
<td>Bob Fish</td>
<td>[email protected]</td>
<td>123-123-1234</td>
</tr>
<td>Shaggy Rogers</td>
<td>[email protected]</td>
<td>509-123-1235</td>
</tr>
<tbody>
</table>
Теперь, если мы будем использовать data-test, как рекомендовано, я бы сделал что-то вроде этого:
<table data-test='users-table'>
...
<tbody>
<tr data-test='user-id-1'>
<td data-test='name-col'>...
<td data-test='email-col'>...
<td data-test='phone-col'>...
Теперь я могу найти td с определенным значением, например
cy.contains('[data-test = "users-table"] [data-test = "name-col"]', user.name).should('be.visible')
или лучше:
cy.get(`[data-test = "users-table"] [data-test = "user-id-${user.id}"]`).within(() => {
cy.get('[data-test = "name-col"]').should('have.text', user.name)
cy.get('[data-test = "email-col"]').should('have.text', user.email)
cy.get('[data-test = "phone-col"]').should('have.text', user.phone)
})
Но в духе «семантической разметки» мне хочется сделать что-то вроде этого:
<table data-entity='users'>
...
<tbody>
<tr data-entity-id='1'>
<td data-col='name'>...
<td data-col='email'>...
<td data-col='phone'>...
</tr>
<tr data-entity-id='2'> ...
Это позволило бы мне не смешивать значения атрибутов data-test = "[noun]-[value]", таких как user-id-1, за счет необходимости придумывать собственный последовательный набор атрибутов data- (data-entity, data-col и т. д.).
Так каков же правильный, объективный и совсем не основанный на мнениях способ использования атрибутов данных? Потому что мы знаем, что разработка программного обеспечения никогда не требует компромиссов и есть только один правильный ответ.
Кроме того, я также начал читать Cypress Testing Library, которая, кажется, может в некоторой степени помочь, извлекая некоторые элементы семантически значимым способом (например, роль или метку), но все равно там будет масса разметки, которая будет не будет покрыто, если, возможно, я не начну ставить role= на все, что кажется грязным хаком и, вероятно, противоречит ARIA или какому-то другому стандарту w3c.
Технически нет ничего плохого в том, как вы это сделали, но это вопрос предпочтений — я бы склонялся к меньшему использованию атрибутов, поскольку команды Cypress довольно хорошо справляются с перемещением по DOM (find, next, eq). По моему мнению, у вас есть излишество, просто пометьте саму таблицу и найдите строку на основе содержимого. (Извините, если это слишком очевидно).
Если вы просто спрашиваете, следует ли мне использовать data-test = "name-col" или data-test = "name", я не уверен, что есть какая-то практическая разница.
не то, должно ли это быть name или name-col, а, скорее, следует ли предпочитать всегда использовать data-test или многочисленные атрибуты данных с большей семантикой, такие как <table data-entity = "users" и <tr data-entity-id='1', для идентификации строки. Возможно, идентификатор никогда не появляется в видимом для пользователя виде td






Обратите внимание, что это основано на моем опыте управления огромными наборами тестов на предприятии и нескольких раз, когда я шел по неверному пути именно по этой проблеме. Здесь есть много компромиссов и нет единственного «правильного пути».
Во-первых, правильно отделить селекторы от внутренних атрибутов DOM, которые являются деталями реализации, такими как атрибут класса и т. д.
Атрибуты данных могут стать частью решения этой проблемы. Однако вы должны знать о недостатках, прежде чем совершать их в одностороннем порядке. Они хороши тем, что вы можете отделить тестовый код от деталей реализации уровня приложения (class) и т. д.
Раньше я видел и использовал такие соглашения, как использование data-cy-component, который представляет тип «компонента», который представляет этот элемент DOM, в сочетании с атрибутами «специфического компонента», связанными с этим компонентом. Например:
<tbody>
<tr data-cy-component = "row" data-cy-row-id = "1">
<td data-cy-component = "cell" data-cy-cell-column = "name">...
<td data-cy-component = "cell" data-cy-cell-column = "email">...
<td data-cy-component = "cell" data-cy-cell-column = "phone">...
Это:
-?).Однако, с отрицательной стороны:
cypress-testing-library — это пример библиотеки, которая поощряет использование селекторов, основанных на данных DOM, которые существуют по соображениям доступности (которые считаются «общедоступными» или видимыми. Его рекомендации, которым отдать предпочтение весьма красноречивы в отношении концептуального мышления.
Это означает использование видимого текста или ролей aria и их атрибутов для решения проблемы. Это дает огромные преимущества:
aria четко определены и являются частью общедоступных стандартов.Обратите внимание, что он не запрещает использование тестовых атрибутов, но также ограничивает вас использованием отдельного атрибута data-testid, исключая описанное выше решение составного атрибута (по крайней мере, без добавления собственных аварийных люков). Но обычно вы обнаруживаете, что вам это все равно не нужно в сочетании с существующими доступными селекторами.
В вашем примере вы сначала должны использовать правильную разметку aria, например:
<table aria-label = "Users table">
<thead>
<tr>
<th id = "name-column">Name</th>
<th id = "email-column">Email</th>
<th id = "phone-column">Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td aria-describedby = "name-column">...
<td aria-describedby = "email-column">...
<td aria-describedby = "phone-column">...
</tr>
<tr> ...
Затем для выполнения различных задач:
cy.findByRole('table', {name: "Users table"}).within(() => {
// Getting a row by its unique id that is visible (email)
cy.findByRole('cell', {description: 'Email', name: '[email protected]'})
.should('exist)
.parent()
.within(() => {
cy.findByRole('cell', {description: 'Name'}) // Getting the name cell for that same row
.should('have.text', 'Shaggy Rogers')
})
})
Также стоит отметить, что таблицы представляют собой особенно более сложный случай. С cypress-testing-library найти и нажать кнопку проще простого. При работе с таблицами (но в любом случае это справедливо для подхода с атрибутами данных) вам, вероятно, захочется зарегистрировать некоторые общие команды.
Теперь, чтобы решить вашу проблему:
но все равно остались бы тонны разметки, которая не была бы покрыта, если бы, возможно, я не начал использовать role= во всем, что кажется грязным хаком и, вероятно, противоречит ARIA или какому-то другому стандарту w3c.
Не ставьте role на что-либо, если это не то, о чем вы говорите. Если да, то вам следует это сделать. Обратите внимание, что вам также не нужно устанавливать role для вещей, которые уже имеют правильный role, поскольку элементы HTML уже имеют разные роли по умолчанию.
С атрибутами арии вы можете зайти невероятно далеко. Если вы чувствуете, что вам нужно больше, это часто происходит потому, что вы пытаетесь сделать что-то, что является плохой практикой, т. е. выбираете что-то невидимое. Но если вам абсолютно необходимо, вы все равно можете использовать базовые атрибуты данных с cypress-testing-library в тех редких случаях, когда вам нужно уйти.
Однако прежде чем это сделать, я обычно добавляю осмысленный aria-label или aria-describedby и использую его. В конце концов, aria-label чего-то должно правильно резюмировать, что это за вещь.
Это новая разметка, но она соответствует стандартам, она выдержит испытание временем и в любом случае необходима для доступа.
И помните, селекторы — это только один уровень надежной абстракции. Это нормально и даже ожидается создание оболочек с общими командами, которые достигают определенных обходов или утверждений.
По моему опыту, это правильный путь. Вы повышаете уверенность в тестах и заставляете думать о правильных вещах.
Я не знал об описанной арией — это выглядит невероятно мощно. Спасибо, что нашли время и напечатали этот очень подробный и полезный ответ, прежде чем веселая полиция — хм — модеры — закрыла мой вопрос!
Я не согласен с этим близко. Это не фундаментальный вопрос, основанный на мнениях — как и во всем, есть компромиссы, и вам нужно решить, что делать дальше.
Немного грустно, что мой подробный ответ был отклонен. Обратная связь при этом была бы лучше!
Да, я не могу поверить в то, что здесь происходит. Видимо, ТАК хорош только для вопросов, на которые вы уже могли найти ответ в Google 🙄
Похоже, вы опечатали строки таблицы
<tr>как<td>.