«Лаборатория Касперского» corporate blog
Java
Development of mobile applications
Development for Android
Kotlin
24 September

Kaspresso: фреймворк для автотестирования, который вы ждали

Все, кто занимался или занимается автотестами под Android, знают, какая это боль.
От объема задач и проблем устаешь так, что даже отпуск не помогает. Люди даже увольняются из-за автотестов.

Боль, страдания и мучения неизбежно приводят к появлению чего-то нового и прекрасного. Мы постарались собрать вместе все грабли, на которые нам пришлось наступить, объединили свои усилия с ребятами из «Авито» и HH и создали то, что сделает ваши отношения с автотестами несравнимо лучше и плодотворнее.

Встречайте: Kaspresso — фреймворк для автотестирования, который вы ждали!



Я сразу отмечу, что в сети доступны два довольно качественных видео, посвященных Kaspresso и AdbServer (вспомогательный для Kaspresso, но в то же время, сильный и независимый проект):

1. Дмитрий Мовчан, Евгений Мацюк — Как начать писать автотесты и не сойти с ума
2. Егор Курников — Единственное, что вам нужно для UI-тестирования

В них подробно описывается история создания библиотек: как мы продвигались от решения одной проблемы к решению другой, и что в итоге получилось. Крайне рекомендую.

Поэтому в статье я расскажу только про основные положения и фишки, чтобы вы быстро могли понять, про что вообще этот фреймворк и какие задачи он решает. А кишки и подробности можно найти в видео и документации.

А зачем нам вообще свой фреймворк?


Каждый разработчик, приступающий к написанию автотестов, неизбежно задается вопросами:

  1. Как начать писать автотесты?
  2. Какие инструменты выбрать?
  3. Что делать, если нужного инструмента нет?
  4. Какие есть лучшие практики?

Ко всему этому прибавьте то, что каждая команда пытается решить эти вопросы как-то по-своему. В результате единого ответа на них нет, негде подсмотреть, как правильно. Поэтому написание и поддержка автотестов обходятся компаниям дорого, что ставит под вопрос целесообразность автоматизации как таковой.

Между тем автоматизация тестирования преследует благие цели. Она позволяет вам иметь всегда зеленый и готовый к релизу мастер. А это значит, что можно выкатить релиз быстро.
Проблема только в поиске ответов на вышеобозначенные вопросы. А ведь они практически одинаковы для всех команд. Так почему бы не автоматизировать их решение?

Что мы хотим от фреймворка?


Давайте немного раскроем наши ожидания от фреймворка.

Хорошая читаемость


По умолчанию в Android нам доступна только библиотека Espresso. Есть еще, конечно, Appium + Cucumber, которые в теории позволяют писать тесты сразу на две платформы. Но комьюнити уверенно движется в сторону именно первого инструмента. Я не буду описывать все «за» и «против» вышеупомянутых библиотек: в сети полно информации об этом. Вот, например, одна из относительно последних ссылок: Appium vs Espresso. Что выбрать и как использовать?

Так вот, Espresso. Неплохой инструмент, но api у него немного вывернутый наизнанку. Взгляните на простенький код:

@Test
fun espressoTest() {
    onView(allOf(allOf(withId(R.id.espresso),
        isDescendantOfA(withId(R.id.coffee_variates))),
        isDescendantOfA(withId(R.id.content))))
        .check(matches(withEffectiveVisibility(View.VISIBLE)))
}

Приходится подумать, чтобы разобраться, не так ли?

Боги из Таиланда и Австралии подарили нам библиотеку Kakao, с которой можно с первого взгляда понять, что происходит в вашем тесте. Вот тот же код, но с Kakao:

@Test
fun kakaoTest() {
    mainScreen {
        myView.isVisible()    
    }
}

Гораздо лучше. Но теперь представьте, что вы автоматизировали целый тест-кейс. Каким примерно будет код?

@RunWith(AndroidJUnit4::class)
class OpenHomeScreenTest: TestCase() {
   @Test
   fun test() { 
      MainScreen() {
         homeButton.click()
      }

      HomeScreen() {
         title {
            isVisible()
            hasAnyText()
         }
      }
   }
}

Главная проблема заключается в том, что, глядя на этот тест, трудно соотнести его с тем тест-кейсом, который вы автоматизировали. Можно, конечно, добавлять логи или что-то в этом роде. Или ввести dsl, которая сразу преобразит внешний вид ваших тестов:

@RunWith(AndroidJUnit4::class)
class OpenHomeScreenTest: TestCase() {
   @Test
   fun test() { 
      step(“1. Open Home screen”) {
         MainScreen() {
            homeButton.click()
         }
      }

      step(“2. Check Home title”) {
         HomeScreen() {
            title {
               isVisible()
               hasAnyText()
            }
         }
      }
   }
}

Согласитесь, совсем по-другому выглядит.

Стабильность


Любая библиотека для ui-тестов «флэкает». Одно и то же действие может выполниться 50 раз успешно, а на 51-м сломаться по непонятной причине. А на 52-м прогоне снова все хорошо. И такие «флэканья» могут прилично подпортить вам нервы.

Мы подумали, почему бы не попробовать перехватывать все действия Kakao-Espresso, и уже туда добавлять дополнительное поведение, направленное на обработку таких вот случайных ошибок.

Именно так и появилась на свет версия 2.1 библиотеки Kakao, позволяющая встраиваться во все вызовы Espresso.

Дополнительно мы создали свои интерсепторы, с помощью которых можно менять поведение в точке вызова или, например, просто логировать. Причем эти интерсепторы — настраиваемые, так что вы можете подстроить их под свои нужды. Подробнее лучше прочитать в доке.

Конкретно в рамках борьбы с flaky-тестами — если какое-то ваше действие выдало исключение, то Kaspresso попробует:

  • Доскроллить. Может, ваша вьюшка просто не видна на экране.
  • Убрать системный диалог, который мог взяться бог знает откуда.
  • Повторить сломавшийся вызов в течение двух секунд.

Этими действиями мы полностью решаем вопрос с flaky-тестами!

Логирование


Одна из главных проблем, которая сопровождает автотесты, — это отсутствие внятного логирования, которого особенно не хватает, когда тест падает. Сиди и гадай, что и как здесь упало.

Благодаря вышеупомянутому интерсептингу мы смогли построить довольно обширную систему логирования.

Давайте взглянем на пример:





По-умолчанию Kaspresso логирует все ваши действия, выделяет каждый шаг теста, выводит время выполнения и т.д.

Полноценный Adb в Espresso-тестах


Изначально adb недоступен в Espresso-тестах. Да, есть adb shell, но там гораздо меньше функций, чем в полном adb. А ведь в нем столько всего, что может нам пригодиться в тестах.

Мы создали отдельную библиотеку AdbServer, которая вернет в ваши тесты полноценный adb! В вышеприведенных видео подробно рассматривается, как мы боролись и через что прошли ради него (раз и два).

Работа с OS Android


Специфика тестов в «Лаборатории Касперского» такова, что нам проходится много работать именно с OS Android: выставлять какие-то настройки, закидывать файлы в систему и т. д. Все это побудило нас стандартизировать всю нашу работу с системой, создав набор понятных интерфейсов, доступных через единую точку входа — класс Device.

Что это за интерфейсы, и что они делают? Давайте я в качестве иллюстрации приведу пару слайдов из презентации Егора:









Документация — здесь.

Под капотом используются в основном AdbServer и UiAutomator.
Но! Если вас вдруг не устраивает имплементация какого-то интерфейса, вы можете задать свою имплементацию через Конфигуратор.

Скриншотилка для DocLoc (Documentation and Localization)


У всех проектов, где есть локализация, часто возникает потребность сделать скриншоты на разных языках, чтобы отдать переводчику в качестве иллюстраций. Ведь очень сложно сделать корректный перевод, не видя, где и как используется конкретная строчка. Поэтому хочется, чтобы можно было делать скриншоты быстро и сразу на всех языках. В том числе — скриншоты системных диалогов. Также могут понадобиться скриншоты на легаси-экранах, причем без глобальных рефакторингов.

Все это позволяет сделать Kaspresso из коробки. Подробнее — читайте в документации.

Архитектура и лучшие практики


Одной из ключевых задач Kaspresso было создание такой dsl, которая бы вас подталкивала к правильной архитектуре тестов и правильному их написанию.

На этой теме было сломано немало копий, ведь подобных правил вы нигде, к сожалению, не найдете. Максимум, что можно найти — статьи о Page Object.

Поэтому мы не пожалели сил и осветили эти вопросы и в документации, и в видео раз и видео два.
Кроме того Саша Блинов написал отличную статью про Kotlin DSL и элегантные тесты. Описанные в статье dsl предоставляются Kaspresso.

Еще на Mobius мы предложили вариант, как можно ускорить отдачу от автотестов и быстро встроить их в PullRequest, минуя неизбежные проблемы с инфраструктурой. Об этом мы подробнее рассказываем здесь.

Как подключить и настроить Kaspresso, если у вас уже много тестов


Главная прелесть в том, что если у вас уже много тестов, написанных на Kakao, и вы захотели внедрить Kaspresso, то вам не нужно ничего переписывать! Просто отнаследуйте ваши классы, в которых описаны тесты, от специального класса TestCase. И все!

Было:

@RunWith(AndroidJUnit4::class)
class OpenHomeScreenTest {
   private val mainScreen = MainScreen()
   private val homeScreen = HomeScreen()

   @get:Rule
   val activityTestRule = 
      ActivityTestRule(MainActivity::class.java, true, false)

   @Test
   fun test() { ... }
}

Стало:

@RunWith(AndroidJUnit4::class)
class OpenHomeScreenTest : TestCase() {
   private val mainScreen = MainScreen()
   private val homeScreen = HomeScreen()

   @get:Rule
   val activityTestRule = 
      ActivityTestRule(MainActivity::class.java, true, false)

   @Test
   fun test() { ... }
}

А если вам не нравится наследование, используйте аналогичный класс TestRule.

Как мы уже упоминали, Kaspresso — очень гибкий и настраиваемый фреймворк. Все настройки доступны через одноименный класс Kaspresso.

По умолчанию используется дефолтная настройка. Если вы хотите что-то кастомизировать, то это будет выглядеть примерно вот так:

@RunWith(AndroidJUnit4::class)
class OpenHomeScreenTest : TestCase(
   Kaspresso.Builder.default().apply {
     viewBehaviorInterceptors.add(MyInterceptor())
     flakySafetyParams.timeoutMs = 1_000
  }
) {
   private val mainScreen = MainScreen()
   private val homeScreen = HomeScreen()

   @get:Rule
   val activityTestRule = 
      ActivityTestRule(MainActivity::class.java, true, false)

   @Test
   fun test() { ... }
}

То есть через конструктор TestCase доступен Kaspresso.Builder, где вы и задаете все необходимые вам настройки. Подробно про конфигуратор написано в документации.

Ближайшие планы


В самое ближайшее время мы планируем добавить следующие вещи:

Отображение шагов теста в Allure (привет ребятам из HeadHunter)


Через специальный интерсептор мы подготавливаем данные для Marathon. Это позволяет нам видеть Allure-репорты следующего характера:



Подробности в PR#4

P.S. PR уже в версии 1.0.1, сейчас готовим соответствующий PR в Marathon.
PSS. Есть мысль еще к каждому шагу прикреплять конкретный кусок лога, а к «упавшему» шагу добавлять скриншот.

Тестирование upgrade-сценариев


Зачастую бывает необходимо проверить корректность апгрейдов приложения. Да, часть проверок можно переложить на юнит-тесты. Но нам бы хотелось быть спокойными за все приложение в целом.

К сожалению, на чистом Espresso это сделать невозможно, так как если мы переустановим тестируемую apk, то тест зафейлится. Можно как-то попробовать пойти на хитрость с раннером, но мне тяжело представить, как будут выглядеть такие доработки и насколько они будут стабильны.
Поэтому в Kaspresso мы готовим решение данной проблемы, основанное на UiAutomator. Однако наверху у вас будет торчать вся та же привычная dsl, крайне похожая на Kakao и с такой же поддержкой интерсептинга.

Полезные ссылки


Kaspresso
AdbServer
Чат, в котором мы всегда будем рады вам ответить на все вопросы

Благодарности


Отдельное спасибо всем, кто принимал участие в становлении проекта.
Это было очень трудно, но чертовски круто!





Вместо заключения


Мы верим в то, что Kaspresso и AdbServer сделают вашу жизнь лучше.
Будем рады вашим отзывам, рекомендациям, ишуям и ПулРеквестам!
И не забудьте поставить звездочку, пожалуйста!

P.S. И в самом конце маленький опрос =)
Есть ли в вашей команде Автотесты?
27.9% Да, используем Espresso 19
13.2% Да, используем Appium и подобные кросс-решения 9
8.8% Да, но еще дополнительно пилим что-то свое поверх существующих решений (или вместо??) 6
27.9% Нет, но в планах 19
22% Нет, господь миловал 15
68 users voted. 19 users abstained.
Начали бы вы использовать Kaspresso прямо сейчас?
13.1% Да, давно уже ждал релиза 8
59% Да, можно попробовать, но слышу впервые 36
14.7% Нет, еще сырой инструмент 9
11.4% Нет, все эти обертки не нужны 7
1.6% Нет, мы не откажемся от своих решений 1
61 user voted. 19 users abstained.
Был бы вам полезен Workshop по автотестам в общем и по Kaspresso в частности
57.4% Да (не забудь добавиться в чатик для доп.инфо) 27
31.9% Нет 15
10.6% Сам могу такой провести 5
47 users voted. 21 user abstained.

+19
7.3k 59
Comments 16
Top of the day