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のインストール方法は公式サイトに書かれています。
ダウンロードするバイナリは、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に保存されます。
sudo chmod +x /usr/local/bin/gitlab-runnercd ~
gitlab-runner installgitlab-runnerのロケールを設定します。~/Library/LaunchAgents/gitlab-runner.plistファイルを開きます。このファイルはgitlab-runner installによって作成されます。
<?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>デフォルトの出力先は、通常ユーザーには書き込み権限がないディレクトリのため、gitlab-runnerが起動しません。書き込みできるように、StandardOutPathとStandardErrorPathの値を書き込み可能なファイルパスに変更します。
<?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>
gitlab-runner startRunnerの登録
MacにインストールしたRunnerをGitLabに登録します。
(1) Admin AreaのRunnersを開きます。
(2) Register an instance runnerをクリックします。次に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 AreaのRunnersページを再読込すると、登録した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) プロジェクトのSettingsのCI/CDを開く。
(2) RunnersのExpandをクリックします。
(3) Enable shared runners for this projectをオンにする。

CI/CDの設定ファイルを追加する
次のように操作して、CI/CDの設定ファイルを追加します。
(1) Webブラウザでリポジトリのページを開き、Set up CI/CDリンクを開く。

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

(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/CDのPipelinesを開きます。失敗したジョブが表示されるので、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が存在しないというエラーが表示されます。
このケースの場合は、次のような原因が考えられます。
tagが間違っている。Shared Runnerを使おうとしているが、プロジェクトでShared Runnerが有効になっていない。- 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/CDのEditorを開くと、.gitlab-ci.ymlファイルをWebブラウザ内で編集してコミットできます。
シミュレータ名やOSバージョンの指定方法については、次の記事を参照してください。











