GitLab CIを活用したXCTestの自動化方法

GitLabにはCI(Continuous Integration: 継続的インテグレーション)機能があります。GitLabのCIを使えば、CI専用の別ソリューションを導入せずに、継続的インテグレーションを行えます。

この記事では、コードをGitLabにプッシュした際に、XCTestを起動し、macOSアプリやiOSアプリのユニットテストを自動実行する設定方法について説明します。

目次

Runnerの登録

GitLabのCI/CDはRunnerが動いて処理を実行します。Runnerにはプロジェクト間で共有する「Shared Runner」と、特定のプロジェクト専用の「Specific Runner」があります。

今回は複数のプロジェクト(リポジトリ)で利用可能な「Shared Runner」を登録する手順を紹介します。

Runnerのインストール

XCTestを実行するために使用するRunnerなので、XCTestを実行するMacにインストールします。ここでは例として、Mac Miniにインストールする手順を説明します。

macOSへのGitLab Runnerのインストール方法は公式サイトに書かれています。

STEP
ダウンロードします。

ダウンロードするバイナリは、Intel Mac版とApple Silicon版とで異なります。ターミナルからcurlでダウンロードします。

  • Intel Mac版
sudo curl --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64"
  • Apple Silicon版
sudo curl --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64"

ダウンロードされたRunnerは/usr/local/bin/gitlab-runnerに保存されます。

STEP
パーミッションを設定し、実行権限を追加します。
sudo chmod +x /usr/local/bin/gitlab-runner
STEP
ホームディレクトリでGitLab Runnerをサービスとしてインストールします。
cd ~
gitlab-runner install
STEP
gitlab-runnerのロケールを設定します。

gitlab-runnerのロケールを設定します。~/Library/LaunchAgents/gitlab-runner.plistファイルを開きます。このファイルはgitlab-runner installによって作成されます。

STEP
次のように環境変数(EnvironmentVariables)を追加します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

<!-- 省略 -->

	<key>EnvironmentVariables</key>
	<dict>
		<key>LC_ALL</key>
		<string>en_US.UTF-8</string>
	</dict>
</dict>
STEP
ログの出力先を変更します。

デフォルトの出力先は、通常ユーザーには書き込み権限がないディレクトリのため、gitlab-runnerが起動しません。書き込みできるように、StandardOutPathStandardErrorPathの値を書き込み可能なファイルパスに変更します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

<!-- 省略 -->

	<key>StandardOutPath</key>
	<string>/Users/USER_NAME/.gitlab-runner/gitlab-runner.out.log</string>
  <key>StandardErrorPath</key>
	<string>/Users/USER_NAME/.gitlab-runner/gitlab-runner.err.log</string>
STEP
サービスを開始します。
gitlab-runner start

Runnerの登録

MacにインストールしたRunnerをGitLabに登録します。

(1) Admin AreaRunnersを開きます。

(2) Register an instance runnerをクリックします。次にShow runner installation and registration instructionsをクリックします。

"Show runner installation and registration instructions"をクリックする
“Show runner installation and registration instructions”をクリックする

(3) 詳細手順が表示されるので、EnvironmentからmacOSを選択し、Command to register runnerという欄の横のコピーボタンをクリックします。コマンドがコピーされます。Closeをクリックして閉じる。

コマンドをコピーする
コマンドをコピーする

(4) RunnerをインストールしたMacのターミナルで、(3)でコピーしたコマンドを実行します。注意点として、XCTestをMac上で実行する際はユーザーモードが必要となるため、sudoを付けずにコマンドを実行します。

実行すると、設定を対話型で聞かれるので、順番に入力します。

(5) GitLabのURLを入力します。

Runtime platform                                    arch=amd64 os=darwin pid=10297 revision=76984217 version=15.1.0
Running in system-mode.                            
                                                   
Enter the GitLab instance URL (for example, https://gitlab.com/):
[http://code.rkdev.corp/]: 

(6) 登録トークンを確認されます。コマンド実行時にすでにトークンを指定しているので、Enterキーを押すだけでOKです。

Enter the registration token:
[-ABCDEFG4567890YZABC]: 

(7) Runnerの説明を指定します。とりあえず、マシン名を入力しました。

Enter a description for the runner:
[MacMini2020.local]: MacMini2020

(8) Runnerに指定するタグを入力します。本記事の例では、Runnerは次の条件に対応しています。

  • Macである
  • Intel Macである
  • OSは常に最新を使う予定である

これらを踏まえて、次の3つのタグを設定しました。

  • mac
  • mac-intel
  • mac-intel-latest
Enter tags for the runner (comma-separated):
mac,mac-intel,mac-intel-latest

(9) メンテナンス用のメモを指定します。とくにないので空欄のままにしました。

Enter optional maintenance note for the runner:

(10) Executerを設定します。公式サイトにmacOSアプリやiOSアプリの場合はshellを使うように書かれているので、shellを指定します。

Registering runner... succeeded                     runner=-ABCDEFG
Enter an executor: custom, parallels, shell, ssh, kubernetes, docker, docker-ssh, virtualbox, docker+machine, docker-ssh+machine:
shell

(11) 登録が成功すると次のように出力されます。

Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

(12) Admin AreaRunnersページを再読込すると、登録したRunnerが表示されます。

登録したRunnerが表示される
登録したRunnerが表示される

CI/CDの設定

テスト用のリポジトリとコードを用意してCI/CDを設定します。

テストコード

ここでは、GitLabのCIによって、XCTestが動くことを確認したいだけなので、次のような必ずエラーになるテストコードと必ず成功するテストコードを入れたユニットテストにしました。具体的な機能などをテストしているわけではなく、XCTestが正常に動作することを確認するためのコードであるため、「テストコード」と呼ぶことに何かしら違和感を覚えるかもしれません。しかし、ここではそのように呼称します。

import XCTest
@testable import CITest

class CITestTests: XCTestCase {

    override func setUpWithError() throws {
    }

    override func tearDownWithError() throws {
    }

    func testExample() throws {
        XCTFail("TEST FAILURE")
    }
    
    func testSuccess() throws {
        XCTAssertTrue(true);
    }
}

Shared Runnerを有効化する

リポジトリ単位で、Shared Runnerを有効化する必要があります。

(1) プロジェクトのSettingsCI/CDを開く。

(2) RunnersExpandをクリックします。

(3) Enable shared runners for this projectをオンにする。

Shared runnerを有効化する
Shared runnerを有効化する

CI/CDの設定ファイルを追加する

次のように操作して、CI/CDの設定ファイルを追加します。

(1) Webブラウザでリポジトリのページを開き、Set up CI/CDリンクを開く。

Set up CI/CD をクリックする
Set up CI/CD をクリックする

(2) Create new CI/CD pipelineをクリックする。

CI/CDパイプラインを作成する
CI/CDパイプラインを作成する

(3) Browse templatesをクリックし、Swift.gitlab-ci.ymlを開き、内容をコピーする。

(4) 自分のリポジトリのPipeline Editorに戻り、コピーした設定を貼り付ける。

(5) Xcodeのプロジェクトファイル名、スキーム名などを変更する。例えば、次の設定ファイルは以下の値を変更しています。

設定
-project CITest.xcodeproj
-scheme CITest
-destination iOS Simulator,name=iPhone 13,OS=15.5
tags mac-intel-latest
-archivePath build/CITest
-archivePath build/CITest.xcarchive
-exportPath build/CITest.ipa
-exportProvisioningProfile “”
paths: build/CITest.ipa
stages:
  - build
  - test
  - archive
  - deploy

build_project:
  stage: build
  script:
    - xcodebuild clean -project CITest.xcodeproj -scheme CITest | xcpretty
    - xcodebuild test -project CITest.xcodeproj -scheme CITest -destination 'platform=iOS Simulator,name=iPhone 13,OS=15.5' | xcpretty -s
  tags:
    - mac-intel-latest

archive_project:
  stage: archive
  script:
    - xcodebuild clean archive -archivePath build/CITest -scheme CITest
    - xcodebuild -exportArchive -exportFormat ipa -archivePath "build/CITest.xcarchive" -exportPath "build/CITest.ipa" -exportProvisioningProfile ""
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  artifacts:
    paths:
      - build/CITest.ipa
  tags:
    - mac-intel-latest

CIによるテストのみが必要で、アーカイブは不要な場合には、archive_projectジョブを削除して、次のような設定ファイルにします。筆者の場合はこちらの設定が必要でした。

stages:
  - build
  - test
  - archive
  - deploy

build_project:
  stage: build
  script:
    - xcodebuild clean -project CITest.xcodeproj -scheme CITest | xcpretty
    - xcodebuild test -project CITest.xcodeproj -scheme CITest -destination 'platform=iOS Simulator,name=iPhone 13,OS=15.5' | xcpretty -s
  tags:
    - mac-intel-latest

-destinationオプションの指定については次の記事を参照してください。

(6) Commit changesをクリックする。

テスト

作成したテストコードをリポジトリにプッシュします。必ず失敗するテストなので、しばらくしてジョブが失敗し、エラー通知のメールが来ます。

プロジェクトのCI/CDPipelinesを開きます。失敗したジョブが表示されるので、failedをクリックします。

失敗したジョブが表示されている
失敗したジョブが表示されている

Failed Jobsタブを開きます。予定通りテスト失敗でエラーになっている場合は、次のように失敗したテストのテストケース(メソッド)名が出力されます。

2022-06-27 18:59:30.903 xcodebuild[18902:109049] [MT] IDETestOperationsObserverDebug: 0.000 sec, +0.000 sec -- start
2022-06-27 18:59:30.903 xcodebuild[18902:109049] [MT] IDETestOperationsObserverDebug: 104.154 sec, +104.154 sec -- end
Failing tests:
	CITestTests:
		CITestTests.testExample()

** TEST FAILED **

ERROR: Job failed: exit status 1

それ以外のエラーが出ている場合は、ログの内容を見て、設定などを見直してください。

UIテスティングについて

この記事ではXCTestを使ったユニットテストしか行っていませんが、UIテスティングについても変わりません。

XcodeのプロジェクトにUIテスティングのテストパッケージが入っていて、スキームのテストプランに登録されていれば、Xcodeから手動でテストするときと同じように、UIテストも自動的に実行されます。

トラブルシューティング

私が設定したときに遭遇したエラーと対応方法を掲載します。

ユーザーがログインしているときしか動かない

XCTestを実行するのはログイン中のカレントユーザーである必要があります。そのため、Runnerを動かすMacはログイン状態でなければなりません。

マシンを再起動したとき

公式の手順にしたがってインストールするとサービスとして登録されます。サービスとして登録することによりマシンを再起動したときも、ログインさえすれば、Runnerが起動します。

状態がpendingのまま

状態がpendingのままとなり、パイプラインのbuild_projectを確認すると、タグに対応したRunnerが存在しないというエラーが表示されます。

このケースの場合は、次のような原因が考えられます。

  1. tagが間違っている。
  2. Shared Runnerを使おうとしているが、プロジェクトでShared Runnerが有効になっていない。
  3. Mac上のRunnerがユーザーモードで動作していない。

2はこのページの「Shared Runnerを有効化する」を参照してください。

3は私がはまりました。Runnerを登録するときに、GitLabが出力したコマンドラインはsudoが付いています。そのまま実行すると、ユーザーモードでRunnerが動かないので、XCTestも動作しません。

シミュレータ名やOSバージョンが間違っている

この場合、build_projectのログの中に次のようなエラーが出力されていて、使用可能なシミュレータとOSの組み合わせが列挙されています。

xcodebuild: error: Unable to find a destination matching the provided destination specifier:

.gitlab-ci.ymlファイル内で指定する値を適切な値に修正しましょう。

プロジェクトのCI/CDEditorを開くと、.gitlab-ci.ymlファイルをWebブラウザ内で編集してコミットできます。

シミュレータ名やOSバージョンの指定方法については、次の記事を参照してください。

著書紹介

Authored Books

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

目次