iOSのUIテストを支援するGoogle謹製ライブラリEarlGreyのバージョン2.0が2018年10月2日に公開されました。まだベータ段階ですが一通り動くようなので、概要やインストール方法などを解説します。
EarlGrey1系について
EarlGrey1系はざっくり言うとXCTest上で動作するUIテスト支援ライブラリです。(XCUITest上ではない)
なぜそんなものが必要なのか。
Xcodeの標準UIテストであるXCUITestはテスト対象のアプリを外側から操作するコードを書くことができるフレームワークです。
このためXCUITestで書いたテストコードは別アプリとして開発端末にインストールされます。
別アプリなので本体アプリ側のUserDefaultsの状態を変更するといった、テストの前提条件を整える処理をテストコードとして書くことができません。
できることはアプリのUIを介した操作だけです。
このことをEarlGreyでは、XCUITestはブラックボックステストだ、と表現しています。
これだとテストの自由度が低すぎますよね。
これを解消するためにEarlGrey1系では、本体アプリ側の処理ができるXCTest上でUI操作やUI要素のアサーションができるライブラリを提供していました。
イメージとしては以下の図のような関係性です。
EarlGrey2.0へ
素晴らしいEarlGrey1系ですが、いくつか解決が困難な課題を持っていました。
例えばiOSのシステムが表示するパーミッションダイアログを操作できなかったりします。
前もってパーミッションの操作をしておくとか運用で逃げることはできる(はず)ですが、あまりイケてないですよね。
そこでEarlGrey2.0では考え方をガラリと変えました。
XCTestで限界があるならば、XCUITestをホワイトボックス化すればいいじゃないか!と方針変更したのです。
ホワイトボックス化をどのように実現しているか。
HelperBundleというターゲットを別途用意し、これを通して本体アプリ側の処理をするアプローチを取っています。
関係性はこんな感じになります。
導入手順
ベータのためCocoapodsやCarthageにはまだ対応していません。(次の四半期でやるよ!って書いてあります)
このためインストールは普通よりも手順が多いです。
またホワイトボックス化の重要な要素であるHelperBundleは個別に用意する必要があるため、パッケージ管理では対応できないんじゃないかなーと思ってたりします。
ほぼ公式マニュアルの日本語訳ですが、誰かの助けになれば幸いです。
以下、例ではEarlGrey2を導入するプロジェクトの名前をWorkEarlGrey2として説明します。
EarlGrey2のインストール
EarlGrey2をダウンロード
git clone --recursive -b earlgrey2 https://github.com/google/EarlGrey.git
を実行。
--recursive
をつけることで、HelperBundleを動かすキーとなるeDistantObject
ライブラリが一緒に落ちてくるので、このままコピペすることをおすすめします。EarlGrey2をXcodeプロジェクトに取り込み
EarlGreyを導入したいXcodeのプロジェクトを開いて、ワークスペースにEarlGrey.xcodeproj
ファイルをドラッグ&ドロップします。
必ずプロジェクト内にドロップしてください。外側に配置されてしまうと別のワークスペースになってしまいます。
EarlGreyのライブラリがビルドできるか確認
うまく取り込めていればXcode左上のビルドターゲットにEarlGreyのライブラリが追加されています。
ここで一度ビルドターゲットを変更して、TestLib
とAppFramework
がビルドできるか確認しておきます。
XCUITestの設定変更
XCUITestターゲットの設定を変更して、EarlGreyが動くようにします。
Build Phases - Link Binary with Libraries で
libTestLib.a
を追加
Build Settings - User Header Search Paths に
EarlGrey.xcodeproj
とeDistantObject.xcodeproj
があるディレクトリを追加
プロジェクトルートからの相対パスで記述できます。例ではプロジェクトルート直下にEarlGreyを落としてきています。-
Build Phases に 新しい Copy Files Phase を追加
-
Swiftでの設定
Swiftプロジェクトの場合はいくつか追加の手順が必要になります。- EarlGrey.swift の追加
SwiftソースからEarlGreyの機能を呼び出せるように、同梱されているEarlGrey.swiftをXCUITestプロジェクト内に追加します。 - Bridging-Header の設定
EarlGreyの機能を呼び出せるように Bridging-Headerを追加します。 EarlGreyで用意しているBridging-Headerファイルを使ってもいいですし、新しく用意してもいいです。(参考:1分で出来る、SwiftにBridging Headerを追加しProjectに反映 - Qiita) 記述内容はEarlGreyで用意しているBridging-Headerファイルを参照ください。 - Runpath Search Path の設定
XCUITestプロジェクトのBuild Settings にあるRunpath Search Paths
に@loader_path/Frameworks
を追加します。
- EarlGrey.swift の追加
XCUITestをビルドして確認
ここまでがいったんXCUITestへのEarlGreyインストール手順となります。
EarlGrey1系で使っていたUI操作の処理などはこの時点で可能です。
テストコードがビルドできるか確認しておきましょう。
HelperBundle の作成
ここからがEarlGrey2.0の肝になるHelperBundleの作成です。
-
HelperBundleターゲットを追加
-
HelperBundleターゲット の Build Settings を以下のように設定
- Base SDK:
Latest iOS
- Other Linker Flags:
-ObjC
を追加 - Bundle Loader:
$(TARGET_BUILD_DIR)/<YOUR_APPLICATION_TARGET_NAME.app>/<YOUR_APPLICATION_TARGET_NAME>
を設定 - User Header Search Paths:
EarlGrey.xcodeproj
とeDistantObject.xcodeproj
があるディレクトリを追加 - (Swiftのみ)RunPath Search Paths:
@loader_path/Frameworks
を設定 - Always Embed Swift Standard Libraries:
Yes
- ※公式マニュアルに記載がないが、サンプルプロジェクトで記載されていたため設定。設定しないとテスト実行時にクラッシュする。
- Enable Bitcode:
No
- ※公式マニュアルに記載がないが、サンプルプロジェクトで記載されていたため設定。設定しなくても問題発生しないが念の為。
- Base SDK:
-
HelperBundleターゲット の Build Phases を以下のように設定
-
XCUITestターゲットの Build Phases で HelperBundle をアプリに組み込む
- XCUITestターゲットの Build Phase に移動
- 左上の+ボタンを押して、
New Copy Files Phase
を選択 - 追加した Copy Files Phase に以下を設定
Bridging-Header を設定
Swiftの場合はHelperBundleでもBridging-Headerが必要となる。
設定方法はXCUITestの設定で記載したものと同じ。
設定内容は以下の通り。(TestLib関連はimportしない)
#import "AppFramework/Action/GREYAction.h"
#import "AppFramework/Action/GREYActionBlock.h"
#import "AppFramework/Action/GREYActions.h"
#import "AppFramework/Matcher/GREYElementMatcherBlock.h"
#import "CommonLib/DistantObject/GREYHostApplicationDistantObject.h"
#import "CommonLib/Matcher/GREYMatcher.h"
HelperBundleを使って、アプリ本体の処理を動かす
これでようやく設定周りは完了で、テストコードに取り掛かれます。
以下のファイルをHelperBundle内に新規作成します。
- SwiftTestsHost.swift
- XCUITestに公開するプロトコル
- HelperBundleとXCUITestの両方に追加する
@objc
protocol SwiftTestsHost {
/// Obtain the host application's interface orientation.
func interfaceOrientation() -> UIInterfaceOrientation
}
- GREYHostApplicationDistantObject+SwiftTestsHost.swift
- アプリケーション側の処理を記述する
extension GREYHostApplicationDistantObject: SwiftTestsHost {
func interfaceOrientation() -> UIInterfaceOrientation {
return UIApplication.shared.statusBarOrientation
}
}
次に、XCTestCaseクラスを拡張します。hostコンピューティッドプロパティをextensionで生やして、SwiftTestsHostで定義されているファンクションを呼び出せるようにします。
書く場所はどこでもいいです。例ではデフォルトで追加されるテストケースファイル内に記述しています。
extension XCTestCase {
/// A variable to point to the GREYHostApplicationDistantObject since casts in Swift fail on
/// proxy types. Hence we have to perform an unsafeBitCast.
var host: SwiftTestsHost {
return unsafeBitCast(
GREYHostApplicationDistantObject.sharedInstance,
to: SwiftTestsHost.self)
}
}
// 以下、省略
これで準備完了。アプリケーション側の処理を動かしてみましょう。
class WorkEarlGrey2UITests: XCTestCase {
func testInterfaceOrientation() {
continueAfterFailure = false
XCUIApplication().launch()
// XCUIApplication().launch() の後に
XCTAssertEqual(host.interfaceOrientation(), UIInterfaceOrientation.portrait)
}
}
サンプルプロジェクト
EarlGrey2.0内にサンプルプロジェクトが同梱されています。
EarlGrey/Tests/FunctionalTests
サンプルとしてきちんとUIテストの実行まで動作するので信頼できます。
インストールするのもマニュアルだけだと辛かったので、この設定と見比べたりして解読しました。
サンプルプロジェクトの構成
Objective-C版もSwift版も両方入っているのですが、私がSwiftをメインで使っているのでSwift版として説明します。(Swiftを取れば、Objective-C版になると思います。多分。)
- FunctionalTestRigSwift
- テスト対象となるアプリです。
- FunctionalSwiftTests
- UIテストターゲットです。
- HostDOCategoriesSwift
- HelperBundleです。
まとめ
EarlGrey2.0ではXCUITestベースに移行したため、レコーディング機能が使えたり、UI操作やアサーションをXCUITestのスタイルで記述することもできます。
その上で本体アプリ側の処理をすることもできるようになり、XCUITestの弱点が見事に補われています。
XCUITestの限界を感じたら、ぜひ試してみてください。