Information

Founded
Website
www.tutu.ru
Employees
201–500 employees
Registered
Pull to refresh
341.01
Rating
Туту.ру
Tutu.ru — сервис путешествий №1 в России.

Прокачиваем Android проект с GitHub Actions. Часть 2

Туту.ру corporate blogIT InfrastructureDevelopment for AndroidDevOps
Tutorial

Запуск UI-тестов на GitHub Actions

Продолжаем разбираться с автоматизацией Android проекта на GitHub Actions, в этой части:

  • Заведем новый проект под UI-тесты в Firebase Test Lab

  • Настроим интеграцию GitHub Actions и Test Lab

  • Посмотрим, как можно запускать UI-тесты в workflow на CI/CD.

Если пропустили первую часть рассказа, где разбирались с Unit-тестами в Android проекте, можно начать с нее.

Чтобы запустить unit-тесты, нам достаточно иметь настроенное Java-окружение. Все тесты проходят очень быстро внутри JVM, всё просто, и почти исключена ситуация появления flacky-тестов. Таких тестов должно быть 70-80% от общего количества тестов в проекте, в первую очередь стоит покрывать ими бизнес-логику.

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

С UI-тестами всё непросто с самого начала. Во-первых, их нужно написать. Звучит банально, но уже на этом этапе у большинства заканчивается энтузиазм. Потом, когда критическая масса UI-тестов написана, нужно придумать, как это богатство запускать и поддерживать в рабочем состоянии в условиях постоянных А/Б-тестов и частых изменений интерфейса. Нужно решать, где они будут запускаться - реальные устройства или эмуляторы и кто будет владеть этими устройствами - своя ферма телефонов или пользоваться услугами сторонних сервисов. В общем, тема не из лёгких.

Мы пойдем по самому простому и удобному пути - Firebase Test Lab

Firebase Test Lab - это сервис от Google, предоставляющий возможность запускать тесты на реальных устройствах или эмуляторах. На момент написания поста бесплатный тариф предлагает 10 тестов в день на эмуляторах и 5 на реальных устройствах. В платном тарифе цена сейчас 1$ за телефоно-час эмулятора, можно и заплатить за такое удобство.

Весь процесс запуска тестов в Test Lab можно описать следующими шагами:

  1. Делаем checkout на нужный коммит и устанавливаем Java-окружение

  2. Проводим unit-тестирование. Если на этом шаге ошибка, то нет смысла тратить время на UI-тестирование.

  3. Собираем специальными Gradle-тасками артефакты для UI-тестирования

  4. Выкачиваем APK-артефакты, которые собираемся отправить на тестирование.

  5. Авторизуемся в Firebase Test Lab с помощью персонального токена.

  6. Используя специальную command line утилиту gcloud, скармливаем в Test Lab собранные ранее APK.

  7. Ждём, когда тесты пройдут, и смотрим результаты в workflow GitHub Actions.

Но для начала заведём на Firebase проект под приложение и сгенерируем себе токен для доступа к нему из GitHub.

Заходим на https://console.firebase.google.com и авторизуемся под своим Google-аккаунтом.

Далее следуем по понятному визарду, нажимаем Создать проект.

Указываем название

Дальше отключаем Google-аналитику или оставляем всё как есть, на Test Lab это никак не повлияет. Если вам нужна аналитика, оставьте и на следующем шаге примите условия пользовательского соглашения.

Когда проект создастся, приступаем к генерации токена для доступа GitHub Actions к Test Lab.

Идём в “Настройки проекта” (“Project settings”), затем на вкладку “Сервисные аккаунты” (“Service accounts”). Там выбираем “Управление правами доступа для сервисных аккаунтов” (“Manage service account permissions”).

Теперь необходимо добавить сервисный аккаунт с теми правами, которые мы планируем использовать на CI/CD в GitHub Actions. Для запуска UI-тестов достаточно прав типа “Редактор”. Подробнее тут.

Заполняем предлагаемые поля

Выбираем тип “Редактор”. Если неправильно настроить права доступа для сервисного аккаунта, то на шаге авторизации в Firebase мы получим ошибку 403.

ERROR: (gcloud.firebase.test.android.run) Unable to access the test environment catalog: ResponseError 403: Not authorized for project ***

На третьем шаге можно просто нажать кнопку “Готово”

Мы только что добавили сервисный аккаунт для CI/CD и теперь готовы получить токен. Выбираем “Создать ключ” (“Create key”).

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

Фокус в том, что в таком виде JSON у нас не получится использовать. Придётся закодировать его через Base64.

Пути два:

1) В консоли вводим

base64 github-actions-sample-key.json > base64-key.txt

Где github-actions-sample-key.json - это название скачанного на предыдущем шаге JSON, а base64-key – файл, в который будет записан результат кодирования.

2) Делаем всё на https://www.base64encode.org/

Возвращается в GitHub проект и записываем результат в Secrets в проект на GitHub.

После добавления ключа в секреты обязательно удалите или перенесите в надёжное место ключ с Firebase и base64-key.

Теперь необходимо добавить в секреты Project ID с экрана общих настроек проекта в Firebase. Не перепутайте с Project number.

Отлично, всё готово к интеграции GitHub Actions и Test Lab. Создаём новый workflow в директории giithub/workflows.

Если прямо сейчас запустить workflow, то на прогонах UI-тестов в Test Lab мы получим ошибку.

ERROR: (gcloud.firebase.test.android.run) User [github-actions-ci-cd@***.iam.gserviceaccount.com] does not have permission to access project [***:initializeSettings] (or it may not exist): Cloud Tool Results API has not been used in project 254361894337 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/toolresults.googleapis.com/overview?project=254361894337 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

Вообще-то можно пройти по ссылке из ошибки и включить API toolresults.googleapis.com, но сейчас посмотрим, как можно управлять вообще любыми API в своём проекте. Нажимаем “Enable APIs and services”.

Тут можно управлять API в проекте и смотреть статистику использования. Находим Cloud Tool Result API и включаем.

Ну теперь-то уж точно всё, пора запускать workflow.

name: UI_tests_on_release
 
on:
  pull_request:
    branches:
      - 'main'
 
jobs:
  assemble_ui_test_artifacts:
    if: startsWith(github.head_ref, 'release/') == true
    name: Build artifacts
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v1
        with: {java-version: 1.8}
 
      - name: Build APK for UI test after Unit tests
        run: |
          ./gradlew test
          ./gradlew assembleDebug
          ./gradlew assembleDebugAndroidTest
 
      - name: Upload app-debug APK
        uses: actions/upload-artifact@v2
        with:
          name: app-debug
          path: app/build/outputs/apk/debug/app-debug.apk
 
      - name: Upload app-debug-androidTest APK
        uses: actions/upload-artifact@v2
        with:
          name: app-debug-androidTest
          path: app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
 
  run_ui_tests_on_firebase:
    runs-on: ubuntu-20.04
    needs: assemble_ui_test_artifacts
    steps:
      - uses: actions/checkout@v2
      - name: Download app-debug APK
        uses: actions/download-artifact@v1
        with:
          name: app-debug
 
      - name: Download app-debug-androidTest APK
        uses: actions/download-artifact@v1
        with:
          name: app-debug-androidTest
 
      - name: Firebase auth with gcloud
        uses: google-github-actions/setup-gcloud@master
        with:
          version: '290.0.1'
          service_account_key: ${{ secrets.FIREBASE_KEY }}
          project_id: ${{ secrets.FIREBASE_PROJECT_ID }}
 
      - name: Run Instrumentation Tests in Firebase Test Lab
        run: |
          gcloud firebase test android models list
          gcloud firebase test android run --type instrumentation --use-orchestrator --app app-debug/app-debug.apk --test app-debug-androidTest/app-debug-androidTest.apk --device model=Pixel2,version=28,locale=en,orientation=portrait

Разбираемся, что тут вообще происходит

Шаг 1

name: UI_tests_on_release
 
on:
  pull_request:
    branches:
      - 'main'
 
jobs:
  assemble_ui_test_artifacts:
    if: startsWith(github.head_ref, 'release/') == true
    name: Build artifacts
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v1
        with: {java-version: 1.8}

Тут точно такие же установки для запуска workflow, что и раньше. Pull request в ветку main из ветки, название которой начинается на release/.

Далее делаем checkout и устанавливаем окружение Java 8.

Шаг 2

- name: Build APK for UI test after Unit tests
  run: |
    ./gradlew test
    ./gradlew assembleDebug
    ./gradlew assembleDebugAndroidTest
 
- name: Upload app-debug APK
  uses: actions/upload-artifact@v2
  with:
    name: app-debug
    path: app/build/outputs/apk/debug/app-debug.apk
 
- name: Upload app-debug-androidTest APK
  uses: actions/upload-artifact@v2
  with:
    name: app-debug-androidTest
    path: app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk

На этом шаге мы прогоняем unit-тесты и после собираем два APK - app-debug.apk и app-debug-androidTest.apk. Почему два? Да просто один APK - это собственно приложение для тестирования, а второй APK содержит instrumentation-тесты, они оба нам понадобятся.

Дальше достаём полученные артефакты по пути и имени в upload-artifact@v2.

Всё это мы уже делали раньше, когда готовили APK к релизу, так что подробно останавливаться не будем.

Шаг 3

run_ui_tests_on_firebase:
  runs-on: ubuntu-20.04
  needs: assemble_ui_test_artifacts
  steps:
    - uses: actions/checkout@v2
    - name: Download app-debug APK
      uses: actions/download-artifact@v1
      with:
        name: app-debug
 
    - name: Download app-debug-androidTest APK
      uses: actions/download-artifact@v1
      with:
        name: app-debug-androidTest

Вторая Job в тестовом workflow запускается не параллельно с первой (assemble_ui_test_artifacts), а ждёт, пока та успешно завершится.

Это указано в строчке.

needs: assemble_ui_test_artifacts

Дальше воспользуемся готовым action download-artifact@v1 и достанем по имени те два APK которые собирали в прошлой Job.

Шаг 4

Добрались до самого интересного - пора передавать артефакты в Test Lab для тестирования.

- name: Firebase auth with gcloud
  uses: google-github-actions/setup-gcloud@master
  with:
    version: '290.0.1'
    service_account_key: ${{ secrets.FIREBASE_KEY }}
    project_id: ${{ secrets.FIREBASE_PROJECT_ID }}
 
- name: Run Instrumentation Tests in Firebase Test Lab
  run: |
    gcloud firebase test android models list
    gcloud firebase test android run --type instrumentation --use-orchestrator --app app-debug/app-debug.apk --test app-debug-androidTest/app-debug-androidTest.apk --device model=Pixel2,version=28,locale=en,orientation=portrait

Сначала выполняем action setup-gcloud, передаем в аргументах ID проекта и тот самый Base64 ключ, который хранится в секретах, всё по инструкции.

Дальше просто выполняем команды в консоли.

Первая gcloud firebase test android models list выведет таблицу из доступных устройств с названиями и версиями SDK. Для тестирования это не требуется, просто удобно посмотреть и выбрать подходящее устройство.

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

Запускаем workflow и смотрим, что получилось.

Отлично, всё работает, самый базовый сценарий запуска UI-тестов реализован! Подробные результаты тестирования можно посмотреть на вкладке Test Lab проекта. Или можно убрать из секретов Project ID (иначе в ссылке будут звездочки вместо ID) и переходить на результаты сразу из логов.

Что ещё можно улучшить в сценарии

Можно включить orchestrator, добавив ключ --use-orchestrator.

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

--num-flaky-test-attempts - для задания количества попыток перезапуска для Flaky тестов.

--network-profile - для задания профиля сети при тестировании. Можно потестировать на медленном соединении, к примеру.

Полный список ключей с описанием тут:

https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run

Еще можно включить шардинг для запускаемых тестов

https://firebase.google.com/docs/test-lab/android/instrumentation-test#sharding

https://github.com/Flank/flank

Идея для самостоятельного изучения если интересно прямо сейчас что-то посмотреть:

1) Попробовать запустить UI-тесты не в Test Lab а в эмуляторе, поднимаемом на MacOS. Можно посмотреть https://github.com/ReactiveCircus/android-emulator-runner

2) Настроить матрицу тестирования для UI-тестов. Запускать тесты не на одной версии Android SDK а на каждой из заданных в условиях.


На этом с запуском UI-тестов из GitHub Actions всё :)

Tags:ci/cdtutorialandroidandroid developmenttestingui testingunit-testinggithubgithub actions
Hubs: Туту.ру corporate blog IT Infrastructure Development for Android DevOps
Rating +14
Views 1.7k Add to bookmarks 34
Comments
Comments 3

Top of the last 24 hours