11.4 KIFを用いた結合テスト

ginrou edited this page May 20, 2013 · 5 revisions

はじめに

  1. iOSについて

  2. Xcode最初のステッフ

  3. 導入

  4. Objective C の基礎

  5. メモリ管理

  6. 1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)

  7. 1.3 UIViewController1 UIViewController のカスタマイズ(storyboard)

  8. UIViewController2 - ModalViewController

  9. UIViewController2 - ModalViewController(storyboard)

  10. UIViewController3 - ライフサイクル

  11. HomeWork 1 Objective C の基本文法

  12. HomeWork 2 UIViewControllerとModalViewController

  13. HomeWork 3 UIViewController + Animation

  14. UIKit 1 - container, rotate-

  15. UINavigationController

  16. UITabController

  17. Custom Container View Controller

  18. Supporting Multiple Interface Orientations

  19. HomeWork 1 - タブバーからモーダルビューを表示する

  20. HomeWork 2 - NavigationController

  21. HomeWork 2.3 デバイスことに回転対応

  22. UIKit 2- UIView -

  23. UIView

  24. UIView のカスタマイズ

  25. UIView Animation

  26. HomeWork 1 - UIScrollView

  27. UIKit 3 - table view -

  28. UITableView について

  29. UITableViewとNavigationController

  30. custom UITableViewCell の作成

  31. UITableViewのその他のオプション、カスタマイズ

  32. HomeWork 1 - Dynamic height with a custom uitableviewcell

  33. UIKit 4 - image and text -

  34. UIImagePickerController

  35. Assets Library

  36. UITextFiled, UITextView

  37. KeyboardNotification

  38. Homework 1 - フォトの複数枚選択

  39. ネットワーク処理

  40. NSURLConnection

  41. JSONのシリアライズとデシリアライズ

  42. UIWebView

  43. ローカルキャッシュと通知

  44. NSUserDefaults, Settings Bundle

  45. NSFileManager

  46. Key Value Observing

  47. NSNotification、NSNotificationCenter を用いた通知

  48. UILocalNotification

  49. Blocks, GCD

  50. Blocks

  51. GCD

  52. 【演習】GCD,-Blocksを用いたHTTPリクエストマネージャの作成

  53. 設計とデザインパターン

  54. クラス設計 1

  55. クラス設計 2

  56. [クラス設計演習] (https://github.com/mixi-inc/iOSTraining/wiki/9.3-%E3%82%AF%E3%83%A9%E3%82%B9%E8%A8%AD%E8%A8%88%E6%BC%94%E7%BF%92)

  57. 開発ツール

  58. Instruments, デバッガ

  59. CocoaPods

  60. テスト

  61. iOS開発におけるテスト

  62. GHUnit

  63. Kiwi

  64. KIF

  65. In-App Purchase

  66. In-App Purchase

  67. 付録

  68. Tips of Xcode

  69. Auto Layout 入門

  70. Auto Layout ドリル

Edit sidebar

Clone this wiki locally

結合テストとは、複数のモジュールを組み合わせ時に行うテストです。iOSにおけるテストは、主に単体テストと結合テストによってなっており、 単体テストについては前章などで紹介した通りですが、結合テストは画面の操作をシミュレートしてテストを行います。

結合テストを行うフレームワークとして、KIFとUIAutomationがあります。それぞれについて特徴をまとめると

UIAutomation KIF
開発元 Instrumentsの機能の一つ Square, Inc が開発
テストの記述方法 JavaScript, シミュレータおよび実機の操作 Objective-C
実行環境 シミュレータ、実機、コマンドライン シミュレータ、実機、コマンドライン
テスト可能な領域※ 実際に動かすことのできる範囲内のみ プログラムとの連携可能

となる。この章では、より広範なテストを行うことのできるKIFについて、その導入法とテスト手法について紹介します。

(※ KIFはソースコードを操作しながらテストを行うことができます。例えばサーバーのレスポンスをモックする、DBを書き換える、などが可能です。これはObjective-Cで書かれたテストのため可能であり、UIAutomationを用いる場合はソースコードからモックすることはできません)

KIFの導入

既にあるプロジェクトに対してKIFを導入するフローを解説します。次のステップで導入することができます。

  1. KIFテスト用のターゲットを追加
  2. Cocoa PodsによるKIFのインストール
  3. appDeleagteの修正

順に追って紹介していきます。

1. KIFテスト用のターゲットを追加

KIFでは、すでに動作するアプリケーションに対してテストを行うので、ターゲットは新規に作るのではなく、アプリケーションのターゲットを複製して利用します。 ターゲットの複製

プロジェクトナビゲーターより、ターゲットを選択して、Duplicate(⌘+D)を行います。アラートが出てきますが、ここでは一番左のDuplicate Only を選択します。(右の選択肢はiPad用に複製する場合です) アラート

ターゲットを追加できたら、ターゲット名などを変更します。名前は適当で構いませんが、今回は "janken KIF" としました。またあわせて build settings → Product name と scheme の名称も変更しました。この二つについては、見栄えの問題なので任意で構いません。

2. CocoaPodsを用いてインストール

インストールにはCocoaPodsを利用します。Podfileは次のようになります。

platform :ios

pod 'KIF', :head

完了したらインストールします

pod install

以前は依存関係が正しく解決できていなかったようですが、2013/05/17現在では問題ありません。(Xcode 4.6.2, OSX 10.8.2)

appDelagateの修正

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

KIFTestControllerはKIFにおけるテストランナーです。KIFTestControllerをサブクラス化して、インスタンスメソッドinitializeScenarios にテストシナリオを追加していきます。ここで追加されたシナリオがテストとして実行されます。

以下にテストコントローラの例を示します

#import "MixiKIFTestController.h"
#import "KIFTestScenario+MixiAdditions.h"

@implementation MixiKIFTestController
- (void)initializeScenarios
{
    // テストシナリオを追加していく
    [self addScenario:[KIFTestScenario myFirstScenario]];
    [self addScenario:[KIFTestScenario mySecondScenario]];
}
@end

Scenario

シナリオとは、上述の通りいくつかのステップを束ねたもので、アプリケーションが仕様通りの動作をすることを確認します。

シナリオを作成するには、KIFTestScenarioのクラス拡張を新たに作成し、その中に記述していきます。 例えば今回のじゃんけんプログラムにおけるシナリオテストでは、次のようなシナリオを考えます。

  1. アリスの"グー"ボタンがタップされる
  2. アリスの"手"のラベルが"グー"に変化する

とても単純です。

シナリオはメソッドとして記述します。例えば今回のシナリオのメソッドを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

Step

ステップは、"一つの処理"、アプリケーションを操作する上では、"一つの動作" に相当します。ここでは、そのステップの作り方と、成否の判定について解説します。

また、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
        }

    }];
}

Accessibility label の指定

ステップの解説において登場した AccessibilityLabel について説明します。 iOSには視覚に障害のある方が利用するときの補助機能として、voice over やヘルプの機能が存在します。その際に各UIパーツについてラベルを設定します。それが AccessibilityLabel であり、KIFの実行時はこのAccessibilityLabelを利用してviewを特定しています。ある特定のパーツをタップするなどの場合は必須の機能になります。その指定方法にはxibを用いた設定法とコード上での指定方があります。

xib上で指定

xibの identity inspector の下の方にAccessibilityという項目があります。Enabledにチェックを入れて、LabelにKIFで指定したい AccessibilityLabel の値を入力します。

xibで指定

コードで指定する

ある UIView のインスタンスでAccessibilityLabelを指定するには、Accessibilityを利用することを表すためにisAccessibilityElementプロパティをYESにし、accessibilityLabelプロパティを設定します。

UIView *someView;
someView.isAccessibilityElement = YES;
someView.accessibilityLabel = @"My Accessibility Label";

なお、UIパーツの中にはこのAccessibilityLabelが利用できないものもあるので注意してください。