UITest

Xcode 7 から UITest という新しい仕組みが導入され、

今までの UI Automation による自動テストより遥かに簡単に UI に関するテストが

簡単に行うことができるようになった。

そこでこの記事では、UITest におけるテストの設計や導入の際に注意する点を挙げる。

なお実際の UITest の書き方やセットアップ方法などは取り上げない。

別プロセスで動く UITest と問題点

UITest は UnitTest とは違い App Target とは別のプロセスで実行される。

そのため、App Target に含まれているファイルにはアクセスできない。 (もちろん UITest 側でコンパイルすれば使えるが…)

なので UITest 側でアプリケーション側に影響を与えるコードは原則 UI に関してのみになる。

そこで問題になるのが、例えばボタンを押して通信成功後に画面遷移を確認したいテストの場合などだ。

本来ならアプリケーションに対して特定の通信に対してスタブを打ちたいところであるが

上記の通りそれは UITest 側からは行えない。

別プロセスで動いているので、もしスタブ処理を UITest 側にターゲット追加したとしても

NSURLSession が動作しないと思う。(未確認)

解決方法

上記の通りアプリケーションのプロセスと UITest のプロセスが分かれている。

しかし2つの間のインターフェースとなるものが一つあリ、それが Launch Arguments である。

それを用いて以下の流れで UITest 側から指定の処理をアプリケーションに振るまわせることが可能になる。

  1. UITest 側で Launch Arguments を指定
  2. アプリケーションを起動
  3. アプリケーションが受け取った Launch Arguments をもとに処理を実行

コード

実際のコードを以下に示す。

UITests.swift

func testSomething() {
let app = XCUIApplication()
app.launchArguments = ["stub"]
app.launch()
}

AppDelegate.m

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let arguments = NSProcessInfo.processInfo().arguments
for argument in arguments {
if argument == "stub" {
//do stubbing
}
}

こうすることで、UITest 側の任意のテストケースにおいて

アプリケーション側で指定の処理を実行することが可能になる。

しかしこうすることで他に問題が発生するので、それの説明と回避策を以下に挙げる。

配布アプリケーションからテストコードを引き剥がす

アプリケーション側で任意のテスト処理を実行できるようになったは良いが、

このままでは App Store へそのままテストコード入りのバイナリが上がってしまうことになる。

OSSなどを使っている場合、万が一 Private API を用いているテストライブラリがあったとすると悲しい。

例えば OHHTTPStubs は、Private API は使ってはいないものの、

申請時はなるべく外してほしい(意訳)と言及している。

スタブを例にとると、スタブ用のデータが静的ファイルなどの場合には申請時にはそのデータをバイナリに含めないようにしないといけない。

そのような場合に取る2つの方法を以下に挙げてこの記事は終了とする。

CocoaPods での Configuration に依存したリンク

以下の用に pod に対して configuration を付けることで

指定の configuration 時にのみインストールすることが可能になる。

pod 'OHHTTPStubs', :configurations => ['Release', 'AdHoc']

Target Membership を動的に変更する

CocoaPods による OSS だけであれば上記で十分だが、

たとえばアプリケーション内にスタブ用の静的ファイルも入れている場合は

UITest で使用する Configuration 時だけ静的ファイルをバイナリに入れるという処理をする必要がある。

その場合には User-Defined Build Setting にフラグを用意し、

Build Phase に Run Script を追加して以下のように流し込む方法があると思う。

if [ ${ENABLE_STUBBING} = 1 ]; then
cp path_to_resource/* ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/

参考