[Android] Github action으로 CI 구축하기

2 분 소요

하루종일 씨름하다 드디어 Android CI를 구축했습니다. 정리할 겸 글을 씁니다. 이곳에서 실제 구동한 흔적을 보실 수 있습니다.

제가 CI에서 진행하는 작업은 다음과 같습니다. 테스트들은 다양한 API level에서 병렬적으로 수행하도록 했습니다.

  • Gradle build
  • Unit test
  • UI test(Espresso)

구현

먼저 .github/workflows/ci.yml과 같이 파일을 만듭니다. 저는 main 브런치에서만 수행하기 위해 앞 부분에 다음과 같이 작성했습니다.

name: Android CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

  ...

그 후 빌드를 진행하는 job을 다음과 같이 만들었습니다. JDK를 셋업하고 ./gradlew build 명령을 수행하고 있습니다.


 ...

jobs:
  build:
    name: Development build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: "11"
          distribution: "temurin"
          cache: gradle

      - name: Build with Gradle
        run: ./gradlew build

그 다음 테스트를 위한 job을 만들었습니다. 다양한 API level에서 수행하도록 했습니다. 유닛 테스트는 ./gradlew test --stacktrace으로 수행합니다. UI 테스트의 경우 reactivecircus/android-emulator-runner를 이용하여 에뮬레이터를 구동시킨 후 ./gradlew connectedCheck 명령으로 진행합니다.


  ...

test:
  name: Tests on Android (API level ${{ matrix.api-level }})
  runs-on: macos-latest
  strategy:
    matrix:
      api-level: [23, 29, 30, 31]
  steps:
    - name: Checkout
      uses: actions/checkout@v3
    - name: Setup JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: "11"
        distribution: "temurin"
        cache: gradle
    - name: Setup Android SDK
      uses: android-actions/setup-android@v2

    - name: Run Unit tests
      run: ./gradlew test --stacktrace
    - name: Run UI test
      uses: reactivecircus/android-emulator-runner@v2
      with:
        api-level: ${{ matrix.api-level }}
        arch: x86_64
        script: ./gradlew connectedCheck

위의 모든 코드를 합하면 다음과 같습니다.


name: Android CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    name: Development build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: "11"
          distribution: "temurin"
          cache: gradle

      - name: Build with Gradle
        run: ./gradlew build

  test:
    name: Tests on Android (API level ${{ matrix.api-level }})
    runs-on: macos-latest
    strategy:
      matrix:
        api-level: [23, 29, 30, 31]
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Setup JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: "11"
          distribution: "temurin"
          cache: gradle
      - name: Setup Android SDK
        uses: android-actions/setup-android@v2

      - name: Run Unit tests
        run: ./gradlew test --stacktrace
      - name: Run UI test
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: ${{ matrix.api-level }}
          arch: x86_64
          script: ./gradlew connectedCheck

삽질

하나의 job에서 테스트 코드까지 수행하려 했으나 UI 테스트만 수행하면 Error: The process '/bin/sh' failed with exit code 1를 내뿜으며 실패했습니다. 그래서 Build와 Test를 수행하는 job을 따로 만들어보니 작동했습니다.

따로 구현 후 테스트를 돌려보니 상위 API level에서는 통과하는데 23 level에서는 실패했습니다. 찾아보니 mockk가 API level 28 미만에서는 특정 기능이 작동하지 않는 것을 알게되었습니다. 이곳에서 Supported features를 확인하면 됩니다. @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)를 테스트 클래스에 달아 해결했습니다.

댓글남기기