Тестирование доступности в Compose: имя, роль, значение

При написании автотестов для приложения не стоит забывать про доступность. Из этой статьи вы узнаете, как написать 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. Затем мы находим нужный компонент по testTagaccessible-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».

🔥 Какой была ваша первая зарплата в QA и как вы искали первую работу? 

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Читать в телеграм

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *