При написании автотестов для приложения не стоит забывать про доступность. Из этой статьи вы узнаете, как написать accessibility-тесты для трех пользовательских компонентов, реализованных с помощью модификаторов clickable, selectable и toggleable.
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Что мы тестируем?
Тесты, которые мы пишем, проверяют наличие у компонентов имени (Name), роли (Role) и значения (Value). Но откуда взялась эта троица? Эти три параметра взяты из рекомендаций по доступности WCAG. В этих рекомендациях есть критерий успеха «Name, Role, Value», который требует, чтобы каждый элемент имел программно определяемые имя и роль. Кроме того, его состояние, свойства и значения, которые пользователь может изменять, должны быть также доступны для программного управления.
Несмотря на название, WCAG применяется не только к вебу, но и к мобильным приложениям — этот стандарт определяет минимальный уровень доступности.
Имя (Name) — это доступное имя элемента, то есть его текстовое представление. Это может быть, например, текст на кнопке, contentDescription у иконки, подпись к переключателю и т.д. Это то, что озвучивается скринридером. Пользователи голосового управления (например, Voice Access) также используют это имя для взаимодействия с элементами.
Роль (Role) — указывает, что это за элемент. Например, если элемент помечен как кнопка (button), ожидается, что он будет вести себя как кнопка. Роль — это обещание того, как элемент должен работать, поэтому если вы указываете роль, обязательно реализуйте и соответствующее поведение. На Android роли используются реже, чем в вебе, но они не менее важны.
Значение (Value) — это состояние, свойство или значение элемента. В зависимости от элементов значение меняется. Например, для чекбокса — это отмечен он или нет, для аккордеона — открыт он или свернут и т.д.
Далее рассмотрим конкретные примеры тестирования критерия «Name, Role, Value» с использованием кастомных компонентов.
Пишем тесты: пример
Мы рассмотрим, как протестировать три элемента: переключатель (switch), группу радиокнопок и кликабельную строку (row).
Тесты в примере довольно простые, в реальных проектах обычно применяются более продвинутые подходы — например, для поиска нужных элементов на экране.
Читайте также: Тест-кейсы для мобильных приложений
Переключаемый компонент (Toggleable)
Первый компонент, который мы протестируем, — это переключатель (switch), как на картинке:

Нам нужно проверить три вещи:
Во-первых, убедиться, что у компонента есть доступное имя — то есть текстовая метка (label) переключателя.
Во-вторых, роль должна быть правильной — компонент должен быть распознан как toggleable.
В-третьих, значение должно быть верным до и после переключения — независимо от того, включен ли переключатель или выключен.
Давайте напишем тест:
class ToggleableTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun hasRoleNameValue() {
composeTestRule.setContent {
ModifiersExampleTheme {
ToggleableScreen()
}
}
val toggleableElement =
composeTestRule.onNode(hasTestTag("accessible-toggle"))
// Assert accessible name
toggleableElement.assertTextEquals("Toggleable")
// Assert role
toggleableElement.assertIsToggleable()
// Assert value
toggleableElement.assertIsOff()
toggleableElement.performClick()
toggleableElement.assertIsOn()
}
}
Сначала необходимо подготовить тест: задать composeTestRule и установить содержимое экрана через setContent. Затем мы находим нужный компонент по testTag — accessible-toggle. После этого наконец переходим к тестам имени, роли и значения.
Тест для проверки имени очень прост: сравниваем текст элемента с ожидаемым — это делается через assertTextEquals. Чтобы проверить роль — есть удобная функция assertIsToggleable. Наконец, значение (то есть текущее состояние переключателя) проверяем с помощью assertIsOff и assertIsOn. Чтобы изменить состояние, вызываем performClick.
Selectable
Следующий компонент, который мы тестируем, — это группа радиокнопок, как показано на картинке:

Для этого компонента нужно проверить, что у обеих опций есть имя (то есть надписи «Option A» и «Option B»), роль — selectable, и значение, отражающее выбран ли элемент.
Тест для этого компонента выглядит так:
class SelectableTest {
@get:Rule
val composeTestRule = createComposeRule()
@Before
fun setup() {
composeTestRule.setContent {
ModifiersExampleTheme {
SelectableScreen()
}
}
}
@Test
fun hasRoleNameValue() {
val selectableElements =
composeTestRule.onAllNodes(hasTestTag("accessible-selectable"))
// Assert accessible name
selectableElements[0].assertTextEquals("Option A")
selectableElements[1].assertTextEquals("Option B")
// Assert role
selectableElements.assertAll(isSelectable())
// Assert value
selectableElements[0].assertIsSelected()
selectableElements[1].performClick()
selectableElements[0].assertIsNotSelected()
selectableElements[1].assertIsSelected()
}
}
Структура очень похожа на предыдущий тест: сначала настройка, затем получение элементов, и далее — проверка имени, роли и значения. Для проверки текста элементов используем ту же функцию assertTextEquals. Аналогично toggleable, для роли и значений в selectable есть функции: isSelectable(), assertIsSelected() и .assertIsNotSelected().
Clickable
Последний кастомный компонент для этой статьи — это кнопка, которую можно использовать для добавления элемента в закладки:

Необходимо убедиться, что у нее есть имя (например, текст «Bookmark this item» как в данном случае), роль кнопки и состояние, которое показывает, добавлен ли элемент в закладки.
Следующий тест это проверяет:
class ClickableTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun hasRoleNameValue() {
composeTestRule.setContent {
ModifiersExampleTheme {
ClickableScreen()
}
}
val clickableElement =
composeTestRule.onNode(hasTestTag("clickable"))
// Assert accessible name
clickableElement.assertTextEquals("Bookmark this item")
// Assert role
clickableElement.assert(
SemanticsMatcher("has correct role") {
it.config.getOrNull(SemanticsProperties.Role) == Role.Button
},
)
// Assert state description
clickableElement.assertStateDescription("Not bookmarked")
clickableElement.performClick()
clickableElement.assertStateDescription("Bookmarked")
}
private fun SemanticsNodeInteraction.assertStateDescription(
stateDescription: String
) =
assert(
SemanticsMatcher("has correct state description") {
it.config.getOrNull(SemanticsProperties.StateDescription) == stateDescription
},
)
}
Настройка и проверка имени снова похожи на предыдущие два теста. Но для проверки роли кнопки нам нужно использовать SemanticsMatcher.
SemanticsMatcher — это инструмент для сопоставления семантических узлов. Нам надо убедиться, что семантическое свойство элемента Role равно Role.Button. Для этого можно обернуть нашу проверку в SemanticMatcher, извлечь роль через it.config.getOrNull(SemanticsProperties.Role) и сравнить значение.
Аналогичная схема работает и для проверки описания состояния элемента. Чтобы не дублировать код, для проверки описания состояния элемента используется расширение assertStateDescription.
Заключение
В этой статье мы рассмотрели написание тестов доступности по критериям успешности WCAG 4.1.2: Имя, Роль, Значение. Эти правила не всегда подходят для мобильных приложений, но эта статья призвана показать, как можно тестировать доступность.
Перевод статьи «Accessibility Tests in Compose — Name, Role, Value».