KASPRESSO. ФРЕЙМВОРК ДЛЯ АВТОТЕСТИРОВАНИЯ
Мацюк Е.В.
Мацюк Евгений Викторович - эксперт-разработчик, направление: Android, Google, г. Москва
Аннотация: в статье рассматриваются основные свойства фреймворка для написания автотестов Kaspresso и причины его появления. Ключевые слова: Android, Kaspresso, ADB, AdbServer, Kotlin, Espresso, UI Automator.
Каждый разработчик, приступающий к написанию автотестов, неизбежно задается вопросами:
- Как начать писать автотесты?
- Какие инструменты выбрать?
- Что делать, если нужного инструмента нет?
- Какие есть лучшие практики?
Ко всему этому прибавьте то, что каждая команда пытается решить эти вопросы как-то по-своему. В результате единого ответа на них нет, негде подсмотреть, как правильно. Поэтому написание и поддержка автотестов обходятся компаниям дорого, что ставит под вопрос целесообразность автоматизации как таковой.
Между тем автоматизация тестирования преследует благие цели. Она позволяет вам иметь всегда зеленый и готовый к релизу мастер. А это значит, что можно выкатить релиз быстро.
Проблема только в поиске ответов на вышеобозначенные вопросы. А ведь они практически одинаковы для всех команд. Так почему бы не автоматизировать их решение?
Что мы хотим от фреймворка?
Давайте немного раскроем наши ожидания от фреймворка.
Хорошая читаемость
По умолчанию в Android нам доступна только библиотека Espresso. Есть еще, конечно, Appium + Cucumber, которые в теории позволяют писать тесты сразу на две платформы. Но комьюнити уверенно движется в сторону именно первого инструмента. Я не буду описывать все «за» и «против» вышеупомянутых библиотек: в сети полно информации об этом. Вот, например, одна из относительно последних ссылок [4]. Что выбрать и как использовать?
Так вот, Espresso. Неплохой инструмент, но api у него немного вывернутый наизнанку. Взгляните на простенький код:
@Test
fun espressoTest() {
onView(allot(allot(withId(R.id.espresso),
isDescendantOfA(withId(R.id.cotfee_variates))), isDescendantOfA(withId(R.id.content)))) .check(matches(withEffeetiveVisibilityiView.VISIBLE)))
Рис. 1. Пример теста на Espresso
Приходится подумать, чтобы разобраться, не так ли? Вот тот же код, но с Kakao [5]:
Рис. 2. Пример теста на Kakao
Гораздо лучше. Но теперь представьте, что вы автоматизировали целый тест-кейс. Каким примерно будет код?
Рис. 3. Пример большого теста на Kakao
Главная проблема заключается в том, что, глядя на этот тест, трудно соотнести его с тем тест-кейсом, который вы автоматизировали. Можно,
конечно, добавлять логи или что-то в этом роде. Или ввести dsl, которая сразу преобразит внешний вид ваших тестов:
@RunWith(AndroidJUrit4::class I class OpenHomeScreenTest: TestCaseO { <§Test
fun testO {
stepfl. Open Home screen") { MainScreenf) {
homeButton.click{ I
>
}
step("2. Check Home title' HomeScreenO { title {
isVisiblel) hasAriyTextf)
>
) {
>
>
Рис. 4. Пример большого теста на Kaspresso
Согласитесь, совсем по-другому выглядит.
Стабильность
Любая библиотека для ui-тестов «флэкает». Одно и то же действие может выполниться 50 раз успешно, а на 51-м сломаться по непонятной причине. А на 52-м прогоне снова все хорошо. И такие «флэканья» могут прилично подпортить вам нервы.
Мы подумали, почему бы не попробовать перехватывать все действия Kakao-Espresso, и уже туда добавлять дополнительное поведение, направленное на обработку таких вот случайных ошибок.
Именно так и появилась на свет версия 2.1 библиотеки Kakao, позволяющая встраиваться во все вызовы Espresso.
Дополнительно мы создали свои интерсепторы, с помощью которых можно менять поведение в точке вызова или, например, просто логировать. Причем эти интерсепторы — настраиваемые, так что вы можете подстроить их под свои нужды. Подробнее лучше прочитать в доке.
Конкретно в рамках борьбы с flaky-тестами — если какое-то ваше действие выдало исключение, то Kaspresso попробует:
- Доскроллить. Может, ваша вьюшка просто не видна на экране.
- Убрать системный диалог, который мог взяться бог знает откуда.
- Повторить сломавшийся вызов в течение двух секунд.
Этими действиями мы полностью решаем вопрос с flaky-тестами. Логирование
Одна из главных проблем, которая сопровождает автотесты, — это отсутствие внятного логирования, которого особенно не хватает, когда тест падает. Сиди и гадай, что и как здесь упало.
Благодаря вышеупомянутому интерсептингу мы смогли построить довольно обширную систему логирования. Давайте взглянем на пример:
I/Viewlnteraction: Checking 'com.kaspersky.kaspresso.proxy.ViewAssertionProxy@95afab5' assertion on vie* I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=activity_main_button_next I/KASPRESSO: single click on AppCompatButton(id=activity_main_button_next;text^lext;) I/KASPRESSO: TEST STEP: "1. Open Simple Screen" in SimpleTest SUCCEED. It took 0 minutes, 0 seconds and
I/KASPRESSO: TEST STEP: "2. Click buttonJL and check button_2" in SimpleTest I/KASPRESSO: single click on AppCompatButton(id=button_l;text=Button 1;)
I/Viewlnteraction: Checking 'com.kaspersky.kaspresso.proxy.ViewAssertionProxy@9f38781' assertion on vie* I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatButton(id=button_2;text=Button 2;) I/KASPRESSO: TEST STEP: "2. Click button_l and check button_2" in SimpleTest SUCCEED. It took 0 minutes,
I/KASPRESSO: TEST STEP: "3. Click button_2 and check edit" in SimpleTest I/KASPRESSO: single click on AppCompatButton(id=button_2;text=Button 2;)
I/Viewlnteraction: Checking 'com.kaspersky.kaspresso.proxy.ViewAssertionProxy@ad01abd' assertion on vie* I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) E/KASPRESSO: Failed to interact with view matching: (with id: com.kaspersky.kaspressample:id/edit) becau I/Viewlnteraction: Checking 'com.kaspersky.kaspresso.proxy.ViewAssertionProxy@d0flc0a* assertion on vie* I/KASPRESSO: Check view has effective visibility=VISIBLE on AppCompatEditText(id=edit;text=Some text;) I/Viewlnteraction: Checking 'com.kaspersky.kaspresso.proxy.ViewAssertionProxy@3b62c7b* assertion on vie* I/KASPRESSO: Check with string from resource id: <2I31558461> on AppCompatEditText(id=edit;text=Some tex I/KASPRESSO: TEST STEP: "3. Click button_2 and check edit" in SimpleTest SUCCEED. It took 0 minutes, 2 s
I/KASPRESSO: TEST STEP: "4. Check all possibilities of edit" in SimpleTest
I/KASPRESSO: TEST STEP: "4.1. Change the text in edit and check it" in SimpleTest I/KASPRESSO: replace text on AppCompatEditText(id=edit;text=Some text;) I/KASPRESSO: tvoe text(lll) on AoDComDatEditText(id=edit:)
Рис. 5. Пример логов на Kaspresso
По-умолчанию Kaspresso логирует все ваши действия, выделяет каждый шаг теста, выводит время выполнения и т.д.
Полноценный Adb в Espresso-тестах
Изначально adb недоступен в Espresso-тестах. Да, есть adb shell, но там гораздо меньше функций, чем в полном adb. А ведь в нем столько всего, что может нам пригодиться в тестах. Мы создали отдельную библиотеку AdbServer, которая вернет в ваши тесты полноценный adb.
Работа с OS Android
Специфика тестов в «Лаборатории Касперского» такова, что нам проходится много работать именно с OS Android: выставлять какие-то настройки, закидывать файлы в систему и т. д. Все это побудило нас стандартизировать всю нашу работу с системой, создав набор понятных интерфейсов, доступных через единую точку входа — класс Device:
Device
Accessibility Activities Apps Exploit Files Keyboard Location Network Permissions Phone
Screenshots
interface Apps {
fun install(apkPath: String) fun uninstall(pckgName: String) fun launch(pckgName: String, data: Uri?) fun openRecent(contentDescr: String) fun kill(pckgName: String)
)
Рис. 6. Интерфейс Kaspresso - Device
Под капотом используются в основном AdbServer и UiAutomator.
Но если вас вдруг не устраивает имплементация какого-то интерфейса, вы можете задать свою имплементацию через Конфигуратор.
Скриншотилка для DocLoc (Documentation and Localization)
У всех проектов, где есть локализация, часто возникает потребность сделать скриншоты на разных языках, чтобы отдать переводчику в качестве иллюстраций. Ведь очень сложно сделать корректный перевод, не видя, где и как используется конкретная строчка. Поэтому хочется, чтобы можно было делать скриншоты быстро и сразу на всех языках. В том числе — скриншоты системных диалогов. Также могут понадобиться скриншоты на легаси-экранах, причем без глобальных рефакторингов. Все это позволяет сделать Kaspresso из коробки.
Архитектура и лучшие практики
Одной из ключевых задач Kaspresso было создание такой dsl, которая бы вас подталкивала к правильной архитектуре тестов и правильному их написанию.
На этой теме было сломано немало копий, ведь подобных правил вы нигде, к сожалению, не найдете. Максимум, что можно найти — статьи о
Page Object. Поэтому данный вопрос максимально освещен в документации [6].
Как подключить и настроить Kaspresso, если у вас уже много тестов
Главная прелесть в том, что если у вас уже много тестов, написанных на Kakao, и вы захотели внедрить Kaspresso, то вам не нужно ничего переписывать. Просто отнаследуйте ваши классы, в которых описаны тесты, от специального класса TestCase.
Было:
@RunWith{AndroidJUnitl:: class) class OpenHomeScreenTest {
private val mainScreen = MainScreenО private val homeScreen = HomeScreenО
@get:Rule
val activityTestRule =
ActivityTestRule(MainActivity:: class.java, true, false)
@Test
fun test(){...>
>
Рис. 7. Пример настройки теста на Kakao
Стало:
@RunWith(AndroidJUnit4::class) class OpenHonieScreenTest : TestCaseO { private val mainScreen = MainScreenO private val homeScreen = HomeScreenO
@get:Rule
val activityTestRule =
ActivityTestRule(MainActivity::class.java, true, false)
@Test
fun test О { ... >
}
Рис. 8. Пример настройки теста на Kaspresso
А если вам не нравится наследование, используйте аналогичный класс TestRule. Как мы уже упоминали, Kaspresso — очень гибкий и настраиваемый фреймворк. Все настройки доступны через одноименный
класс Kaspresso. По умолчанию используется дефолтная настройка. Если вы хотите что-то кастомизировать, то это будет выглядеть примерно вот так:
@RunWith(AndroidJUnit4:¡class) class OpenHomeScreenTest : TestCase( Kaspresso.Builder.default)).apply { viewBehaviorlnterceptors.add{MyInterceptor()) flakySafetyParams.timeoutMs = 1_000
> ) {
private val mainScreen = MainScreenO private val homeScreen = HomeScreenO
@get:Rule
val activityTestRule =
ActivityTestRule(MainActivity::class, java, true, false)
@Test
fun testO { ... >
>
Рис. 9. Пример конфигурации теста на Kaspresso
То есть через конструктор TestCase доступен Kaspresso.Builder, где вы и задаете все необходимые вам настройки. Заключение
Также в Kaspresso были имлементированы другие очень полезные фичи. как поддержка Allure [7], поддержка Robolectric [8], обертка над UI Automator - Kautomator [9].
Таким образом, в статье были рассмотрены причины создания и фичи фреймворка для написания автотестов под Android - Kaspresso.
Список литературы
1. Егор Курников. На чем писать Android UI-тесты. [Электронный ресурс], 2021. URL:Режим доступа: https://habr.com/ru/companv/avito/bloa/516650/ (Дата дата обращения: 19.11.2021).
2. Документация Espresso. [Электронный ресурс]-]^ 2021. Режим доступа:
URL:-https://developer.android.com/trainina/testina/espresso/ -(Дата-дата
обращения: 19.11.2021).
3. Документация UI Automator. [Электронный ресурс]-^-]^ 2021. Режим доступа: URL:—https://developer.android.com/trainina/testina/ui-automator/ (Дата дата обращения: 19.11.2021).
Отформатировано: Отступ: Слева: 0 см, Выступ: 0,5 см
4. Алексей Емелин. Appium vs Espresso. Что выбрать и как использовать? [Электронный ресурс].-]. 2021. Режим доступа: URL:
httDs://www.voutube.com/watch?v=A1 Xzcs Fec/ —(Дата—дата обращения: Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17)) Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17))
19.11.2021). 5. Библиотека Kakao. [Электронный ресурс]-]. 2021. Режим доступа: URL: httDs://aithub.com/KakaoCuü/Kakao/ -(Дата дата обращения: 19.11.2021).
6. Раздел документации Kaspresso "Как писать автотесты". [Электронный ресурс].-]. 2021. Режим доступа: URL:
.https://qithub.com/KasperskvLab/Kaspresso/blob/master/wiki/04 How to write autotests. Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17))
md/ -(Дата дата обращения: 19.11.2021). 7. Раздел документации Kaspresso "Поддержка Allure". [Электронный ресурс].-]. 2021. Режим доступа: URL:
https://qithub.com/KasperskvLab/Kaspresso/blob/master/wiki/09 Kaspresso-Allure.md/ Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17))
(Дата дата обращения: 19.11.2021). 8. Раздел документации Kaspresso "Поддердка Robolectric". [Электронный ресурс].-]. 2021. Режим доступа: URL:
https://qithub.com/KasperskvLab/Kaspresso/blob/master/wiki/08 Kaspresso- Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17))
Robolectric.md/ (Ддата обращения: 19.11.2021). 9. Раздел документации Kaspresso "Kautomator". [Электронный ресурс].-], 2021. Режим доступа: URL:
https://qithub.com/KasperskvLab/Kaspresso/blob/master/wiki/02 Wrapper over UiAutom Отформатировано: Основной шрифт абзаца, Шрифт: (по умолчанию) Arial, 11 пт, Цвет шрифта: Другой цвет (RGB(17;17;17)) Отформатировано: Отступ: Слева: 0,5 см, без нумерации
ator.md/ -(Дата дата обращения: 19.11.2021).