jarinosuke blog

about software engineering, mostly about iOS

KIF を使った iOS Integration Test

久しぶりにテストを書く

このブログも独自ドメインではてなブログに移行して読者数が一気に減ってしまいましたが、半年以上前のホッテントリぶりにブログ書きます。 前回は iOS 開発環境における CI 導入についてでしたが、今回はもう一度テストに戻って、その中でも iOS における Integration Test の実行方法について書きたいと思います。

f:id:jarinosuke0808:20121111212805p:plain

Integration Test って?

日本語では良く結合テストと呼ばれていますが、iOS 開発ガイドなどでは単体テストをロジックテストと呼び、結合テストはアプリケーションテストと呼んでいます。 結合テストは UI テストとも呼ばれる場合もあったり、このブログで紹介した GHUnit でも最近では ViewController のテストができたりと境界は結構曖昧です。 僕の iOS におけるテストのイメージとしては、 Unit Test はコンポーネント毎のロジックのためのテストであり、 Integration Test はアプリケーション全体の振る舞いの確認のテストすなわちシナリオテストだと考えています。

Test-Driven iOS Development (Developer's Library)

Test-Driven iOS Development (Developer's Library)

Integration Test のための Framework

iOS で Integration Test を行うための Framework はたくさんありますが、どれもまだ成熟していないのが現状です。 その中でも比較的技術の習得と導入が用意で開発者に優しいのが、Square が提供している KIF と呼ばれる Framework です。 この Frameworkの特徴を挙げていきます。

UIAutomation を使った Integration Test ではシナリオ毎に JavaScript を用意しないといけないのに対して、iOS 開発者にとってこれは大きなメリットです。しかし Integration Test を実行する人が非 iOS 開発者である場合だとこれはデメリットとなり、逆に実際の操作を元にシナリオを自動生成してくれる UIAutomation を用いた方が良いかもしれません。

  • 他のツールに依存しない

他の多くの iOS 向け Integration Test Framework は他言語への依存やそれに即したツールへの依存がありますが、既存のプロジェクトと Xcode のみで完結します。なるべく環境を汚したくない、そんな要望にはうってつけです。

  • CI 導入が簡単

Square は Zapp という KIF 用の CI ツールも用意してくれています。全てが今手を置いているローカル Mac 上で完結するのでビルドサーバやリモートレポジトリなども用意する必要はありません。

  • Square の運用実績あり

カード決済を主とする会社の運用実績があるので安心です。実際の Square アプリで動作している動画もブログで紹介されています。(このブログの URL カッコいい)

というわけでここまで来たら導入しない手は無いでしょう。

KIF 導入

  • 既存のプロジェクトに submodule として KIF.framework を追加
cd /path/to/MyApplicationSource
mkdir Frameworks
git submodule add https://github.com/square/KIF.git Frameworks/KIF

もし git を使えない状況であるなら、 Github からダウンロードして .Frameworks/KIF 配下に置きましょう。

  • KIF プロジェクトを workspace へ追加

workspace に KIF を追加してメインプロジェクトから KIF を使えるようにしましょう。左端のナビゲーションバーに KIF.xcodeproj ファイル をドラッグ&ドロップで完了です。もし workspace が現在無い場合は Xcode が新しく作るかどうか聞いてくるので作りましょう。その後に workspace 名も入力します。

f:id:jarinosuke0808:20121111212005p:plain

  • テストターゲットの作成

既存のプロジェクトに KIF が有効なビルドターゲットを作成しなくてはいけません。このようにテストターゲットを分ける事で、テストコードを App Store 申請用のビルドに混入させてリジェクトされてしまう悲しい事故から身を守ってくれます。 既存のプロジェクトのナビゲーションバーからプロジェクトを選択し、新しいターゲットは既存のターゲットを duplicate させて作成しましょう。右クリック(もしくはCmd + クリック)をして duplicate を選択します。

f:id:jarinosuke0808:20121111212050p:plain

そうすると iPad 用にターゲットを duplicate するかどうか聞かれますが、Duplicate Only を選択します。

f:id:jarinosuke0808:20121111212125p:plain

新しいターゲットの名前を分かりやすいように Integration Test などに変更します。

f:id:jarinosuke0808:20121111212144p:plain

Product Name も変えると良いです。

f:id:jarinosuke0808:20121111212207p:plain

  • テストターゲットの設定

テストターゲットの Build Phases タブを開き、Link Binary With Libraries の + ボタンを押し libKIF.a を追加します。

f:id:jarinosuke0808:20121111212228p:plain

次に KIF のヘッダーファイルにアクセスできるようにします。Build Settings タブを開き、Headers Search Paths に以下を追加します。

$(inherited)
$(SRCROOT)/Frameworks/KIF

f:id:jarinosuke0808:20121111212254p:plain

同様にして Other Linker Flags にも以下を追加します。

-ObjC
-all_load

f:id:jarinosuke0808:20121111212313p:plain

最後にプリプロセッサにテストターゲット用のフラグを追加します。これによってリリースビルドにテストコードが含まれないことが保証されます。

$(inherited)
RUN_KIF_TESTS=1

f:id:jarinosuke0808:20121111212331p:plain

少し長くなりましたが、これで KIF の導入は完了です。

KIF のテストコード

KIF のテストコードは先ほども述べた通り、全て Objective-C で書く事ができます。具体的にはテストを実行するためのコントローラ、テストのシナリオ、シナリオを形作るステップの3種類のファイルから構成されるコードになります。 シナリオは複数のステップで構成され、コントローラの初期化時に加えられたそれらのシナリオが順番にテストケースとして一つずつ実行されます。

テストコード説明

では実際にテストコードをみていきましょう。 まずはテストコントローラのイニシャライザです。このクラスは KIFTestController を継承して作成します。初期化メソッド内でシナリオが生成されテストの準備が行われます。

- (void)initializeScenarios;
{
    [self addScenario:[KIFTestScenario scenarioToLogIn]];
    
    [self addScenario:[KIFTestScenario scenarioToSelectChat]];
    
    [self addScenario:[KIFTestScenario scenarioToAddMessage]];
}

次にシナリオです。シナリオは KIFTestScenario のカテゴリとして実装します。シナリオ毎にクラスメソッドを実装しましょう。

+ (id)scenarioToLogIn;

+ (id)scenarioToSelectChat;

+ (id)scenarioToAddMessage;

ログインのシナリオについて実装部分もみてみます。

+ (id)scenarioToLogIn;
{
    KIFTestScenario *scenario = [KIFTestScenario scenarioWithDescription:@"Test that a user can successfully log in."];
    [scenario addStep:[KIFTestStep stepToReset]];
    [scenario addStep:[KIFTestStep stepToEnterText:@"user@example.com" intoViewWithAccessibilityLabel:@"username"]];
    [scenario addStep:[KIFTestStep stepToEnterText:@"thisismypassword" intoViewWithAccessibilityLabel:@"password"]];
    [scenario addStep:[KIFTestStep stepToTapViewWithAccessibilityLabel:@"login"]];
    
    return scenario;
}

シナリオに対して複数のステップを加えてる事が分かりますね。 それぞれのステップについては KIF 側で用意してくれているものであり、自分で実装する必要はありません。 どんなステップがあるか知りたい人は以下の KIFTestStep を見てみると良いと思います。

テストコードの説明は以上になります。

終わりに

Github に今回使用したテストプロジェクトを置きました。 workspace まるごとコミットするやり方が分からなかったので、元プロジェクトのみです…。 もし挙動などが気になるのであればチェックアウトして上の手順で workspace を作成して試してみてください。 あと、Accesibility をオンにするのを忘れないようにしましょう。

一つ一つのシナリオをコードで書くのは手間のかかる作業だと思いますが、毎回人が行う苦労を考えると圧倒的に便利です。 是非使ってみてください!