GithubActionsでiOSのUnitTestをしてみた

GithubActionsでiOSのUnitTestを自動で走らせるCIを作成してみました。
思ったよりも苦戦したので苦戦した箇所も合わせて記述しておきます。

GithubActionsでiOSのUniTestを動かす

GithubActionsでCIを動かすためのymlを作成します。
.git/workflow/UnitTest.yml を以下のコードで作成しましょう。

name: UnitTest
run-name: ${{ inputs.title }} - @${{ github.ref_name }}

on:

  workflow_dispatch:
    inputs:
      title:
        required: true
        type: string
        default: "UnitTest"
        description: "Title"

jobs:
  unit_test:
    runs-on: macos-last
    timeout-minutes: 25

    steps:
      # checkout
      - name: checkout
        uses: actions/checkout@v4

      # sw_vers
      - name: sw_vers
        run: sw_vers

      # list devices
      - name: list devices
        run: xcrun simctl list devices

      # unit test
      - name: unit test
        run: xcodebuild -project MyProject.xcodeproj -scheme MyProject -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14,OS=16.2'  test

      # xcresult
      - name: xcresult
        uses: kishikawakatsumi/xcresulttool@v1
        with:
          path: TestResults.xcresult
        if: success() || failure()

設置ができたら

GithubのAction > UnitTest > Run workflow▼

から実行可能です

xcodebuild: error: Unable to find a destination matching the provided destination specifierというエラーがでる

xcodebuild: error: Unable to find a destination matching the provided destination specifier:
{ platform:iOS Simulator, OS:16.5, name:iPhone 14 }

実行する上記のようなエラーが出る場合があります。
指定したシュミレーターの端末とOSが見つからないよというエラーらしいです。

そこで使えるiOSと端末を調べる必要がでてきます。
そんなときは

$ xcrun simctl list devices

このコマンドでシュミレーターの一覧を表示することができます。
もちろんGithubActionsでも可能です。

今回は事前に設定しておきました。
このように出るので使用できる端末とOSを確認したらテストの部分を変更しましょう。

今回はiPhone 15 iOS17.2にします。

修正前

run: xcodebuild -project {プロジェクト名}.xcodeproj -scheme {スキーム名} -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name={端末},OS={OS}'  test

修正後

run: xcodebuild -project MyProject.xcodeproj -scheme MyProject -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2'  test

しかしこれでもエラーが治らない時があります。
色々調べたところ runs-on: macos-latest が実は最新のmacosをとってこない場合があることがわかりました。
そこでmacosに関してはバージョンを指定することで回避します。

修正前

runs-on: macos-latest

修正後

runs-on: macos-14

これでOKですが念の為macのバージョンを表示して確認することにしましょう。

$ sw_vers

macOSのバージョン確認方法です。
今回はすでにymlに記述しています。

% sw_vers
ProductName:		macOS
ProductVersion:		14.4
BuildVersion:		23E214

このようにバージョンが表示されるので確認しましょう。
私の環境ではmacos-14、iPhone 15、iOS 17.2でうまくいきました。

Resource not accessible by integrationというエラー

Resource not accessible by integration

次にこのようなエラーが出た場合ですが、これはテスト結果をみやすくするライブラリ「kishikawakatsumi/xcresulttool@v1」に関するエラーです。
権限の問題なのでGithubの権限を変更しましょう。

GithubのSetting > Action > General > Workflow permissions > Read and write permissions

に変更する必要があります

Cannot test target “TestProjectUITests” on “My Mac”: UI tests are not supported on My Macというエラー

Cannot test target “TestProjectUITests” on “My Mac”: UI tests are not supported on My Mac

xcodebuildのtestを走らせるとUnit TestとUI Testの両方が走りUI Testの方で失敗していまいます。
私の場合はUI Testは不要だったのでUnit Testだけに絞ることで回避しました。

UI Testを含めるとテストが数十分以上かかるので不要な場合はUnit Testだけに絞る方がすぐに終わるのでおすすめです。

Unit Testだけにする場合は以下のようにコードを修正する必要があります。

修正前

run: xcodebuild -project MyProject.xcodeproj -scheme MyProject -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2'  test

修正後

xcodebuild -project TestProject.xcodeproj -scheme TestProject -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' '-only-testing:MyProjectTests' test

'-only-testing:MyProjectTests'を追加し、MyProjectTestsフォルダ以下にあるテストのみを実行するように修正しました。

'-only-testing:MyProjectTests/AAATests'のようにさらに絞ったり、特定のテストだけを実行することも可能のようです。

覚えておくと便利かもしれません。

最終コード

name: UnitTest
run-name: ${{ inputs.title }} - @${{ github.ref_name }}

on:

  workflow_dispatch:
    inputs:
      title:
        required: true
        type: string
        default: "UnitTest"
        description: "Title"

jobs:
  unit_test:
    runs-on: macos-14
    timeout-minutes: 25

    steps:
      # checkout
      - name: checkout
        uses: actions/checkout@v4

      # sw_vers
      - name: sw_vers
        run: sw_vers

      # list devices
      - name: list devices
        run: xcrun simctl list devices

      # unit test
      - name: unit test
        run: xcodebuild -project TestProject.xcodeproj -scheme TestProject -resultBundlePath TestResults -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' '-only-testing:TestProjectTests' test

      # xcresult
      - name: xcresult
        uses: kishikawakatsumi/xcresulttool@v1
        with:
          path: TestResults.xcresult
        if: success() || failure()