-
Notifications
You must be signed in to change notification settings - Fork 336
11.4 KIFを用いた結合テスト
結合テストとは、複数のモジュールを組み合わせ時に行うテストです。iOSにおけるテストは、主に単体テストと結合テストによってなっており、 単体テストについては前章などで紹介した通りですが、結合テストは画面の操作をシミュレートしてテストを行います。
結合テストを行うフレームワークとして、KIFとUIAutomationがあります。それぞれについて特徴をまとめると
UIAutomation | KIF | |
---|---|---|
開発元 | Instrumentsの機能の一つ | Square, Inc が開発 |
テストの記述方法 | JavaScript, シミュレータおよび実機の操作 | Objective-C |
実行環境 | シミュレータ、実機、コマンドライン | シミュレータ、実機、コマンドライン |
テスト可能な領域※ | 実際に動かすことのできる範囲内のみ | プログラムとの連携可能 |
となる。この章では、より広範なテストを行うことのできるKIFについて、その導入法とテスト手法について紹介します。
(※ KIFはソースコードを操作しながらテストを行うことができます。例えばサーバーのレスポンスをモックする、DBを書き換える、などが可能です。これはObjective-Cで書かれたテストのため可能であり、UIAutomationを用いる場合はソースコードからモックすることはできません)
既にあるプロジェクトに対してKIFを導入するフローを解説します。次のステップで導入することができます。
- KIFテスト用のターゲットを追加
- Cocoa PodsによるKIFのインストール
- appDeleagteの修正
順に追って紹介していきます。
KIFでは、すでに動作するアプリケーションに対してテストを行うので、ターゲットは新規に作るのではなく、アプリケーションのターゲットを複製して利用します。
プロジェクトナビゲーターより、ターゲットを選択して、Duplicate(⌘+D)を行います。アラートが出てきますが、ここでは一番左のDuplicate Only を選択します。(右の選択肢はiPad用に複製する場合です)
ターゲットを追加できたら、ターゲット名などを変更します。名前は適当で構いませんが、今回は "janken KIF" としました。またあわせて build settings → Product name と scheme の名称も変更しました。この二つについては、見栄えの問題なので任意で構いません。
インストールにはCocoaPodsを利用します。Podfileは次のようになります。
platform :ios
pod 'KIF', :head
完了したらインストールします
pod install
以前は依存関係が正しく解決できていなかったようですが、2013/05/17現在では問題ありません。(Xcode 4.6.2, OSX 10.8.2)
KIFによるテストは、appDelagateの -application:didFinishLaunchingWithOptions:
にテスト開始のメソッドを追加することでテストを自動実行しています。
そのための下準備として、テストのコントローラを追加します。
新規ファイル作成より、KIFTestControllerを継承したクラスを作成します。ヘッダファイルはそのままで、.mファイルに次のようにinitializeScenarios
を追加します
@implementation JankenTestController
- (void)initializeScenarios
{
}
@end
今回クラス名はJankenTestControllerとしました 以上でテストコントローラの作成は終了です。実際にテストを行う際は以下にテストシナリオを追加して行きます。
次にappDeleagteを修正します。ヘッダをインクルードしている箇所に、次のスニペットを挿入します。
#if RUN_KIF_TESTS
#import "JankenTestController.h"
#endif
そして、 -application:didFinishLaunchingWithOptions:
内に次のコードを挿入します。
#if RUN_KIF_TESTS
[[JankenTestController sharedInstance] startTestingWithCompletionBlock:^{
exit([[JankenTestController sharedInstance] failureCount]);
}];
#endif
以上で準備は完了です。スキーマをjanken KIF に変更して実行します。シミュレータが起動してすぐに終了すれば導入は完了です。
KIFによる結合テストはシナリオベースで行います。シナリオベースとはどういうことか、例えば次のような例が挙げられます。
ログインにおけるシナリオ
- まず、メールアドレス欄にメールアドレスを、パスワード欄にパスワードをそれぞれ入力する
- 次に、ログインボタンを押し認証が始まる
- そして認証が成功しログイン後の画面が表示される
このように、順々に処理が行われる物をひとまとめにして シナリオ といいます。一方で、「メールアドレスを入力する」、「ログインボタンを押す」、のような一つ一つの処理を ステップ といいます。複数のステップをまとめてシナリオとして、アプリケーションがシナリオ通りに動作するかをテストするのがKIFによる結合テストです。
それぞれの動作が正しく行われているかは、各ステップごとに判断を行います。次項以降で、シナリオとステップの書き方について解説を進めて行きます。
KIFTestControllerはKIFにおけるテストランナーです。KIFTestControllerをサブクラス化して、インスタンスメソッドinitializeScenarios
にテストシナリオを追加していきます。ここで追加されたシナリオがテストとして実行されます。
以下にテストコントローラの例を示します
#import "MixiKIFTestController.h"
#import "KIFTestScenario+MixiAdditions.h"
@implementation MixiKIFTestController
- (void)initializeScenarios
{
// テストシナリオを追加していく
[self addScenario:[KIFTestScenario myFirstScenario]];
[self addScenario:[KIFTestScenario mySecondScenario]];
}
@end
シナリオとは、上述の通りいくつかのステップを束ねたもので、アプリケーションが仕様通りの動作をすることを確認します。
シナリオを作成するには、KIFTestScenarioのクラス拡張を新たに作成し、その中に記述していきます。 例えば今回のじゃんけんプログラムにおけるシナリオテストでは、次のようなシナリオを考えます。
- アリスの"グー"ボタンがタップされる
- アリスの"手"のラベルが"グー"に変化する
とても単純です。
シナリオはメソッドとして記述します。例えば今回のシナリオのメソッドをaliceLabelChangeScenario
とすると、このメソッドは次のようにかけます。
+ (id)aliceLabelChangeScenario
{
KIFTestScenario *scenario = [KIFTestScenario scenarioWithDescription:@"This is my first scenario"]; // シナリオを新規作成
[scenario addStep:[KIFTestStep stepToTapAliceRockButton]]; // ステップ 1. アリスの"グー"ボタンがタップされる
[scenario addStep:[KIFTestStep stepToWaitForAliceLabelChanged:@"グー"]]; // ステップ 2. アリスの"手"のラベルが"グー"に変化する
return scenario;
}
シナリオにステップを追加するにはaddStep:
を利用します。シナリオ開始時のsetupなども用意されているので、詳細が気になる場合はこちらをご覧ください。
https://github.com/square/KIF/blob/master/Classes/KIFTestScenario.h
ステップは、"一つの処理"、アプリケーションを操作する上では、"一つの動作" に相当します。ここでは、そのステップの作り方と、成否の判定について解説します。
また、KIFのフレームワークにもいくつかのStepが含まれており、当然それらを利用することもできます。 例えば次のようなステップが含まれています。
ステップ名 | 説明 |
---|---|
+ (id)stepToWaitForViewWithAccessibilityLabel:(NSString *)label | 指定したAccessibilityLabelを含むviewが現れるのを待つ |
+ (id)stepToWaitForAbsenceOfViewWithAccessibilityLabel:(NSString *)label | 指定したAccessibilityLabelを含むviewが消えるのを待つ |
+ (id)stepToWaitForTappableViewWithAccessibilityLabel:(NSString *)label | 指定したAccessibilityLabelを含む、タップ可能なviewが現れるのを待つ |
+ (id)stepToWaitForTimeInterval:(NSTimeInterval)interval description:(NSString *)description | 指定した時間待つ |
+ (id)stepToWaitForNotificationName:(NSString*)name object:(id)object | 指定したnotification name の NSNotificationが発火するのを待つ |
+ (id)stepToTapViewWithAccessibilityLabel:(NSString *)label | 指定したAccessibilityLabelを含むviewをタップする |
+ (id)stepToEnterText:(NSString *)text intoViewWithAccessibilityLabel:(NSString *)label | 指定したAccessibilityLabelを含むviewにテキストを入力する |
他にも様々なステップがあります。詳細については
https://github.com/square/KIF/blob/master/Classes/KIFTestStep.h
こちらをご覧ください。
独自にステップを定義して利用したい場合もあるかと思います。そのようなときは、+ (id)stepWithDescription:(NSString *)description executionBlock:(KIFTestStepExecutionBlock)executionBlock
を利用します。
引数の一つであるKIFTestStepExecutionBlockの中に独自のステップを定義します。このブロックはKIFTestStepResultを返すようになっており、そのステップが成功したか、失敗したか、あるいは待つのか、を定義します。
例えば、"アリスの手ラベルが、引数として渡された値に変化するステップ"は次のように書くことができます。
+ (id)stepToWaitForAliceLabelChanged:(NSString *)expectedString
{
return [KIFTestStep stepWithDescription:@"aliceの手のラベルが変化した" executionBlock:^KIFTestStepResult(KIFTestStep *step, NSError *__autoreleasing *error) {
// 指定したAccessibilityLabelを含むviewを取得する
UIAccessibilityElement *elem = [[UIApplication sharedApplication] accessibilityElementWithLabel:@"alice_hand"];
UILabel *label = (UILabel *)[UIAccessibilityElement viewContainingAccessibilityElement:elem];
if ([label.text isEqualToString:expectedString]) {
return KIFTestStepResultSuccess; // 引数の文字列とテキストの文字列が一致する場合は Success
} else {
return KIFTestStepResultFailure; // 引数の文字列とテキストの文字列が一致しない場合は Failure
}
}];
}
ステップの解説において登場した AccessibilityLabel について説明します。 iOSには視覚に障害のある方が利用するときの補助機能として、voice over やヘルプの機能が存在します。その際に各UIパーツについてラベルを設定します。それが AccessibilityLabel であり、KIFの実行時はこのAccessibilityLabelを利用してviewを特定しています。ある特定のパーツをタップするなどの場合は必須の機能になります。その指定方法にはxibを用いた設定法とコード上での指定方があります。
xibの identity inspector の下の方にAccessibilityという項目があります。Enabledにチェックを入れて、LabelにKIFで指定したい AccessibilityLabel の値を入力します。
ある UIView のインスタンスでAccessibilityLabelを指定するには、Accessibilityを利用することを表すためにisAccessibilityElementプロパティをYESにし、accessibilityLabelプロパティを設定します。
UIView *someView;
someView.isAccessibilityElement = YES;
someView.accessibilityLabel = @"My Accessibility Label";
なお、UIパーツの中にはこのAccessibilityLabelが利用できないものもあるので注意してください。
はじめに
-
導入
-
1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)
-
UIKit 1 - container, rotate-
-
UIKit 2- UIView -
-
UIKit 3 - table view -
-
UIKit 4 - image and text -
-
ネットワーク処理
-
ローカルキャッシュと通知
-
Blocks, GCD
-
設計とデザインパターン
-
開発ツール
-
テスト
-
In-App Purchase
-
付録