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. И в самом конце маленький опрос =)

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

    Есть ли в вашей команде Автотесты?

    Начали бы вы использовать Kaspresso прямо сейчас?

    Был бы вам полезен Workshop по автотестам в общем и по Kaspresso в частности

    «Лаборатория Касперского»
    424,03
    Ловим вирусы, исследуем угрозы, спасаем мир
    Поделиться публикацией

    Комментарии 12

      +1
      Супер, спасибо за крутой инструмент!
      Мы в Райффайзен Банке как раз планируем на него подсесть. Как раз переводим тесты с Appium на нативные, по этому сложно было проголосовать в первом вопросе опросника)
        0

        Зачем вообще нужен любой сторонний фреймворк при работе с adb? Что там происходит такого, чего я не могу сделать сам, используюя только adb?

          –2
          посмотрите видео, пожалуйста
          там раскрывается этот вопрос
            +7

            Ненавижу видео. Можно раскрыть его здесь? Чтобы я мог задавать уточняющие вопросы, чтобы мог использовать поиск, чтобы мог цитировать.

              –1
              Например вы не можешь скачать что-то с компьютера (например, с помощью adb, что было бы логично) находясь внутри Espresso тестов.
              Почему? Да потому что во время прогона тестов, ваша тестовая apk понятия не имеет о внешнем мире и вообще не в курсе подключена она к какому-то компьютеру или нет.

              Вы не можете менять показания датчиков/сенсоров на эмуляторе — потому что это закрытая вещь, к которой доступ строго через adb/telnet
              Именно поэтому пришлось создавать AdbServer.

              Ненавижу писать комментарии, тут одно пустословие — лучше взгляните код и поймете что к чему, его скомпилировать можно и посмотреть что он делает. А также подправить если вам покажется что-то не так.
                0
                Я же спросил
                Что там происходит такого, чего я не могу сделать сам, используюя только adb?

                Всё названное я могу делать, используя только adb. И даже могу пинать через него своё espresso приложение, дёргая нужные прямо сейчас классы.

                А ответ мне даётся на какой-то другой вопрос, который я, вроде, не задавал.
                  +2
                  Здесь какое-то непонимание того, для чего вообще AdbServer создавался. Это не замена adb — это и есть adb, поэтому разумеется вы можете делать все то же самое с помощью adb — поскольку это оно и есть.

                  Идея была в том, чтобы во время тестов вызывать нужные adb команды непосредственно из самих тестов. Нужно посередине теста файл на устройство скинуть? — дошли в тесте до нужно экрана и скинули.

                  Обычными средствами из самого теста вы не сможете пользоваться adb
                0
                v1sar — автор концепции, он отлично все распишет.
                но реально, в видео все рассказываем
                никто не мешает делать цитаты оттуда и уже более предметно пообщаться
            0
            Поставлю в план изучения и освоения на второе место.
            0
            подскажите плиз, а как протестировать нажатие на пункт recylerview?

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

            Самое читаемое