[Android] GitHub Actions로 CI에서 UI 테스트 수행하기

2025. 11. 14. 14:43·개발/Android

들어가며

스타카토의 CI에서 UI 테스트를 추가한 이유

https://github.com/woowacourse-teams/2024-staccato

 

GitHub - woowacourse-teams/2024-staccato: 🗺️ 지도 기반 일상 기록 서비스 스타카토

🗺️ 지도 기반 일상 기록 서비스 스타카토. Contribute to woowacourse-teams/2024-staccato development by creating an account on GitHub.

github.com

 

이전에 로그인 화면에서 닉네임 입력 형식에 대한 피드백을 제공하는 기능을 추가했고, 입력 값에 따라 올바른 피드백이 제공되는지 확인하기 위한 UI 테스트를 작성했다.

UI 테스트가 추가됨에 따라, 다른 테스트와 마찬가지로 UI 테스트를 CI 워크플로에서 실행시켜 코드 병합 시 기능의 동작을 검증할 필요성이 생겼다. 이에 CI 단계에서 UI 테스트를 실행할 수 있도록 워크플로를 수정했다.

이번 포스팅은 GitHub Action을 이용해 UI 테스트를 실행하는 워크플로를 작성하고 개선시킨 과정에 대해 간단히 설명한다.

 


 

UI 테스트를 실행하기 위한 GitHub Action

UI 테스트를 실행하려면 실기기 또는 가상 에뮬레이터 기기가 필요하다. 이는 GitHub 원격 레포지토리 환경에서도 마찬가지이다. 당연하지만 원격 환경에서는 실기기를 연결할 수 없으니 가상 애뮬레이터 기기를 실행해야한다.

우리가 직접 개발하는 환경에선 Android Studio와 같은 IDE의 도움을 받아 쉽게 에뮬레이터 기기를 실행하고 관리할 수 있지만 원격 레포지토리 환경에서는 어렵다. 감사하게도 몇몇 개발자 분들이 이를 쉽게 도와주는 Action을 개발해주셨다.

 

https://github.com/ReactiveCircus/android-emulator-runner

 

GitHub - ReactiveCircus/android-emulator-runner: A GitHub Action for installing, configuring and running hardware-accelerated An

A GitHub Action for installing, configuring and running hardware-accelerated Android Emulators on macOS virtual machines. - ReactiveCircus/android-emulator-runner

github.com

 

android-emulator-runner Action은 GitHub Actions 환경에서 에뮬레이터 기기의 설정과 실행, 테스트 스크립트 실행 등의 작업을 자동화하고 쉽게 관리할 수 있도록 도와준다.

이것 말고도 여러 다른 Action이 있지만, 필자는 해당 Action이 많은 개발자들이 사용하고 있고 레퍼런스도 많다고 생각해 이 Action을 골랐다. 참고로 Coil, Retrofit, Leak Canary, Robolectric 등의 여러 오픈소스에서도 해당 Action을 사용해 CI 환경에서 계측 테스트를 실행하고 있다.

 

 

CI 워크플로에서 UI 테스트 실행하기

기본 사용 방법

android-emulator-runner Action의 사용 방법은 어렵지 않다. 가장 기본적인 사용 예제는 아래와 같은 형식이다.

jobs:
  ui-tests:
    steps:
      - name: Run UI Tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 29
          script: ./gradlew connectedCheck

 

Action을 사용할 때 필수로 지정해야하는 속성으로는 두 가지가 있다.

  • api-level : 실행할 안드로이드 에뮬레이터의 API 레벨 지정
  • script : 해당 에뮬레이터에서 실행할 테스트 명령어 또는 Shell 스크립트

여기서 script에 들어간 "./gradlew connectedCheck" 명령어는 모든 연결된 기기(connected, 실제 기기나 에뮬레이터)에서 수행 가능한 테스트들을 실행하는 Gradle Task이다. 아래 세 개의 Task를 모두 포함한다.

  • connectedAndroidTest (Instrumentation test / UI test)
  • connectedDebugAndroidTest
  • connectedReleaseAndroidTest

 

이렇게 원하는 API 레벨을 설정하고 UI 테스트를 실행할 명령어만 추가하면 CI 환경에서 UI 테스트를 어려움 없이 실행시킬 수 있다.

 

좀 더 잘 사용하기

여러 가지 설정과 옵션을 추가하면 UI 테스트를 더욱 효율적으로 실행할 수 있다. 몇 가지 유용한 설정들을 간단하게 소개하겠다.

 

KVM 가속 설정

CI를 실행해보면, 에뮬레이터가 설정되고 실행되는 시간이 제법 걸린다. CI를 실행할 때마다 조금씩 다르지만, 평균적으로 6분 이상 소요되는 것 같다. 

만약 워크플로의 Runner가 리눅스(ubuntu-latest)라면, KVM을 활성화하는 코드를 추가해 에뮬레이터의 실행 속도를 높일 수 있다.

KVM(Kernel-based Virtual Machine)이란, 리눅스 커널 안에 내장된 하드웨어 가상화 기능이다.
에뮬레이터(가상 머신)를 빠르게 돌릴 수 있도록 CPU의 가상화 기능을 직접 사용한다.

 

아래는 Action 레포지토리에 소개된 KVM 기능 활성화 코드이다.

    steps:
      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

 

위 step을 UI 테스트를 실행하는 Job에 그대로 붙여 넣으면 된다. (UI 테스트를 실행하는 step 이전에 실행시키면 된다.)

ubuntu-latest Runner에서 위 코드를 추가하여 KVM을 활성화하면, 에뮬레이터의 실행 속도가 macOS Runner에서의 에뮬레이터 실행 속도보다 약 2~3배 빨라진다고 한다.

 

CI를 실행할 때마다 에뮬레이터의 실행 시간이 달랐기 때문에 필자는 정확한 시간 차이를 측정해보지 못했다. 하지만 체감상으로 위 코드를 적용한 경우 에뮬레이터의 실행 시간이 약 1~2분 정도 더 빨라진 것 같았다.

 

유용한 옵션들

Action을 사용할 때 추가하는 속성을 중에서도 여러 유용한 옵션이 존재한다. 그 중 일부 옵션들을 정리해보았다. 아래의 옵션들은 필수가 아닌 선택 값이므로 상황에 따라 필요한 옵션만 선택하면 된다.

  • target : 시스템 이미지를 설정한다. 쉽게 이해하자면 OS의 버전이다. desktop, wear, tv, playstore가 설치된 버전 등의 다양한 버전의 이미지를 선택할 수 있다. 만약 위치, 구글 맵, 알림(FCM), 블루투스 등과 같은 구글 API를 활용한다면 google_apis를 선택하면 된다.
    • e.g. default, google_apis, google_apis_playstore, android-wear, android-tv, google_atd, android-automotive, android-desktop 등
    • 더 다양한 이미지는 Action 깃허브 페이지를 확인해보거나, 'sdkmanager --list' 명령어를 이용해 확인할 수 있다.
      • 참고로 해당 명령어는 Android SDK 명령어이며, 사용하기 위해서는 사전에 SDK 설치와 올바른 환경 변수 설정이 필요하다.
      • 만약 명령어를 사용할 수 없다면 SDK 설치 여부와 환경 변수 설정 여부를 확인해보자.
  • profile : 에뮬레이터를 생성할 때 하드웨어 기기 종류를 설정할 수 있다. 여기에 기기 ID(문자열)를 입력하면 해당 기기의 디스플레이 스펙으로 에뮬레이터가 가동된다.
    • 기기 ID를 작성할 때는 큰 따옴표("")로 감싸야 한다. (e.g. profile: "pixel_6")
    • 필자는 소형 휴대폰과 일반 크기의 휴대폰, 태블릿 PC 3가지 디스플레이에서 테스트하고자 "pixel_4", "pixel_6", "Nexus 7" 세 가지 프로필을 사용하고 있다.
    • 더 다양한 기기 종류와 기기 ID를 알고 싶다면, 콘솔 창 또는 터미널에서 'avdmanager list device' 명령어를 입력해 확인할 수 있다.
      • 해당 명령어 또한 Android SDK 명령어이며, 사용하기 위해서는 사전에 SDK 설치와 환경 변수 설정이 필요하다.
  • avd-name : 가상 에뮬레이터 기기의 이름을 지정할 수 있다. 기본값은 "test"이다.
  • emulator-boot-timeout : 에뮬레이터의 가동 시간 제한을 설정할 수 있다. 초 단위로 지정한 시간을 넘어가면 fail을 반환하여 해당 step이 실패한다. 기본 값으로 600초가 지정되어 있다.
  • force-avd-creation : 동일한 avd-name으로 지정된 AVD를 덮어쓰고 새로 생성할지 말지 설정한다. 기본 값으로 true로 설정되어있으며, 이 경우 동일한 avd-name을 가지고 있다면 AVD를 강제로 생성하여 덮어쓴다.
  • disable-animation : AVD의 애니메이션 비활성화 여부를 설정한다. 기본 값으로 true로 설정되어 있다.
  • emulator-options : 에뮬레이터의 옵션을 지정할 수 있다. 소리 설정, 카메라(전면/후면), 부팅 애니메이션 설정 등 다양한 속성을 지정할 수 있다.
    • 기본적으로 설정된 에뮬레이터 속성 : "-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim"
    • 더 다양한 속성은 터미널 또는 콘솔 창에 다음과 같은 명령어를 입력해서 확인할 수 있다 : 'emulator -help-properties'
      • 해당 명령어 역시 Android SDK 명령어이며, 사용하기 위해서는 SDK 설치, 올바른 환경 변수 설정이 필요하다.
  • working-directory : script 옵션의 명령어를 실행할 디렉토리 경로를 지정할 수 있다. 만약 워크플로의 job 단계에서 working-directory를 따로 지정하지 않았거나, 또는 이전에 지정된 경로와 명령어를 실행할 경로가 다를 때 사용된다.
    • 기본 값은 ./ 이다.
    • e.g. 현재 워크플로의 루트 경로가 ./android 이고, 안드로이드 프로젝트가 ./android/MyProject 와 같은 하위 디렉토리에 있을 경우, working-directory 옵션으로 ./android/MyProject 로 경로를 지정한다.
이 외에도 더 다양한 옵션에 대한 설명은 ReactiveCircus/android-emulator-runner 에서 확인할 수 있다.

 

Matrix 활용하기

더 다양한 Android API 레벨과 기기 프로필에서 동일한 UI 테스트를 실행하고 싶을 수 있다. 하지만 여러 개의 Job을 직접 생성하면 yml의 코드가 길어지고 관리가 번거로워진다.

이 때 matrix strategy를 활용하면 코드 중복을 줄일 수 있다.

jobs:
  ui-tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        api-level: [21, 29, 32]
        target: [default, google_apis]
        profile: ["pixel_6", "Nexus 7"]

    steps:
      - uses: actions/checkout@v4

      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Run UI Tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: ${{ matrix.api-level }}
          target: ${{ matrix.target }}
          profile: ${{ matrix.profile }}
          arch: x86_64
          script: ./gradlew connectedCheck

 

위 코드에서는 총 12개의 Job이 동시에 실행된다. (api-level 3가지 * target 2가지 * profile 2가지 = 12개의 Job)

물론 위 예제는 저렇게 활용할 수 있다는걸 보여주는 단순 예제일 뿐이다. 위 예제처럼 너무 많은 설정을 matrix에 추가하는 것은 과할지도 모른다.

 

UI 테스트를 실행하는 목적과 상황은 다르다. 화면 크기가 다른 상황에서 UI에 대한 테스트가 필요하거나, 또는 각각 다른 API 레벨에서 테스트하는 것이 중요할 수 있다. 이러한 상황을 고려해서 정말 필요한 속성만을 지정해 matrix를 실행하도록 하여 자원 낭비를 줄이는 것이 좋겠다.

 


 

마치며

android-emulator-runner Action의 레포지토리를 확인하면, 위에서 설명한 내용 외에도 다양한 상황에서 에뮬레이터를 설정하고 실행할 수 있는 예제 코드를 볼 수 있다. NDK와 CMake를 활용하는 방법(아마 AI 모델을 활용해야 하는 경우 등에 필요할 수 있다.), Gradle 캐싱을 이용해 에뮬레이터 부팅 속도를 향상시키는 방법 등이 예제 코드로 설명되어 있다.

또한 해당 Action을 사용하는 오픈소스의 GitHub 레포지토리 링크도 함께 첨부되어 있다. Coil, Retrofit 등과 같은 유명 라이브러리에서는 어떻게 이 Action을 설정하고 활용하는지 엿볼 수 있다.

필자도 android-emulator-runner 레포지토리와 여러 유명 오픈소스의 워크플로 코드를 살펴보면서 우리 프로젝트의 입맛에 맞는 CI 스크립트를 구성할 수 있었다. 여러분들도 이런 정보들을 잘 참고한다면 각자의 상황에 적합한 효율적인 스크립트를 작성할 수 있을 것이다.

 

지금까지 UI 테스트를 CI 워크플로에서 실행하는 방법을 가볍게 알아보았다.

일반적으로는 UI 테스트의 비중이 단위 테스트와 통합 테스트에 비해 낮고 중요도 또한 낮을 수 있지만, 애플리케이션의 주요 기능을 실제 사용 환경과 가까운 환경에서 테스트할 수 있으며, 특히 사용자와의 상호작용(텍스트 입력 등)에 대한 테스트가 가능하다는 점에서 UI 테스트도 중요한 테스트라고 생각한다. 그러므로 UI 테스트 또한 CI 단계에서 수행하고 관리되어야 하는 자원이다.

이 글에서 소개한 Action을 활용해 UI 테스트를 편리하게 관리해보자!

 

참고 자료

  • ReactiveCircus/android-emulator-runner

 

'개발 > Android' 카테고리의 다른 글

[Android] AndroidManifest.xml은 무엇일까?  (0) 2025.09.01
[Android] GitHub Actions로 QA용 CD 구축하기 - Firebase 활용  (0) 2025.08.03
[Android] Compose에서 Pinch Zoom 구현하기  (0) 2025.06.13
[Android] GitHub Actions로 CI 적용하기 - 간단 가이드  (0) 2025.04.02
[Android] Compose 상태 관리 심화  (0) 2025.02.15
'개발/Android' 카테고리의 다른 글
  • [Android] AndroidManifest.xml은 무엇일까?
  • [Android] GitHub Actions로 QA용 CD 구축하기 - Firebase 활용
  • [Android] Compose에서 Pinch Zoom 구현하기
  • [Android] GitHub Actions로 CI 적용하기 - 간단 가이드
호두가코딩했어요
호두가코딩했어요
몰입과 소통을 좋아하는 안드로이드 새싹 개발자입니다. 모르는 것은 이해할 때까지 파고들어서 공부해야 적성이 풀립니다. 더 나은 구현을 위해 고민하고 자주 소통하는 것을 좋아합니다. 분야에 개의치 않고 학습하고 지식을 쌓아서, 훌륭한 서비스를 만들어가는 개발자가 되고자 합니다.
  • 호두가코딩했어요
    WalnutTheDeveloper
    호두가코딩했어요
  • 전체
    오늘
    어제
    • 카테고리
      • 개발
        • CS
        • Kotlin
        • Android
      • 일상
        • 회고록
        • 맛집
  • 블로그 메뉴

    • 홈
    • 카테고리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    소프트웨어 아키텍처 패턴
    코루틴
    아키텍처 패턴
    coroutine
    MVVM
    디자인 패턴
    코틀린_코루틴의_정석
    coroutinedispatcher
    compose
    소프트웨어 디자인 패턴
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
호두가코딩했어요
[Android] GitHub Actions로 CI에서 UI 테스트 수행하기
상단으로

티스토리툴바