連載
» 2014年04月04日 18時00分 公開

Kiwi+CocoaPodsで始めるiOSアプリの振る舞いテスト入門iOSアプリ開発でもCI/継続的デリバリしようぜ(2)(3/4 ページ)

[諏訪悠紀,アンダースコア]

テストケースの作成を支える便利なメソッド

 Kiwiには、テストケースを構成する際に役に立つさまざまなメソッドがあります。例えば、各contextメソッドを実行する前に共通の処理を行いたい場合に使うメソッド(beforeメソッド)や、一時的に保留にしておきたい場合に使うメソッド(pending_メソッド)などがあります。これらのメソッドをうまく使うと、無駄のないテストケースを効率的に作成できます。

beforeAll/afterAllメソッド

 beforeAllメソッドは同じブロック内の全てのテストケースの実行前に1度だけ呼び出されるメソッドです。

 またafterAllメソッドは同じブロック内の全てのテストケースの後に1度だけ呼び出されるメソッドです。ある1つの条件下でテストしたい場合、全てのテストケースに対する事前処理を1つのメソッドで済ませられます。

 次の例では、beforeAllメソッドでNSUserDefaultsにデータを登録し、afterAllメソッドでNSUserDefaultsの全てのデータを消去しています。

beforeAll(^{
    // 事前処理
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:@"Sample" forKey:@"name"];
    [defaults setObject:@10 forKey:@"count"];
    [defaults synchronize];
});
afterAll(^{
    // 事後処理
    NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
    [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
});
context(@"stringForKey:の場合", ^{
    it(@"nameが「Sample」であること", ^{
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSString *name = [defaults stringForKey:@"name"];
        [[name should] equal:@"Sample"];
    });
});
context(@"objectForKey:の場合", ^{
    it(@"countが10であること", ^{
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSNumber *count = [defaults objectForKey:@"count"];
        [[count should] equal:@10];
    });
});

 また、各テストケースの実行前/実行後に呼び出されるbeforeEachメソッド/afterEachメソッドもあります。テストケースごとにデータベース内のデータを初期化したい場合などに活用できます。

specify/pending_メソッド

 通常、describeメソッドやcontextメソッドなどといったテストケース用のメソッドでは第1引数にテストの説明文となる文字列を渡しますが、specifyメソッドはテストの説明の記述を行わないテストケースです。第1引数にブロックを渡すだけでテストケースとして実行されます。

specify(^{
    NSUInteger a = 16;
    NSUInteger b = 26;
    [[theValue(a + b) should] equal:theValue(42)];
});

 また、pending_メソッドは実際には実行されないテストケースです。対象とする機能が未実装の場合などに使用します。

pending_(@"未実装の機能", ^{
    // 実行されないテストケース
});

テスト結果を比較する「マッチャー」とは

 Kiwiには、テスト結果を比較するためのマッチャーが数多く存在しています。よく使うマッチャーを確認していきましょう。

マッチャーの基本的な書き方

 まず、マッチャーの基本的な書き方を理解しましょう。一番シンプルな例は次の通りです。

NSString *name = @"Sample";
[[name should] equal:@"Sample"];
[[name shouldNot] equal:@“Test"];

 上記はnameというオブジェクトに対してのテストですが、Kiwiにおける値のテストはshouldマクロ(またはshouldNotマクロ)と比較用のメソッド(equal:メソッドなど)の組み合わせで表現します。上記の例はそれぞれ「nameの値は『Sample』であること」というテストと、「nameの値は『Test』ではないこと」というテストになります。

値のテスト

 値をテストするマッチャーは特に使う機会が多いと思いますが、Kiwiでは次のようなマッチャーが用意されています。

マッチャー 説明
[[subject should] equal:(id)anObject] 指定した値と同じ値であるか(isEqual:による比較)
[[subject should] beNil] nilであるか
[[subject should] beIdenticalTo:(id)anObject] 指定した値と同じ値であるか(==演算子による比較)
[[subject should] equal:(double)aValue withDelta:(double)aDelta] ある値との差の絶対値が指定した値より小さいか
[[subject should] beWithin:(id)aDistance of:(id)aValue] ある値との差の絶対値が指定した値より小さいか
[[subject should] beLessThan:(id)aValue] 指定した値より小さい値であるか
[[subject should] beGreaterThan:(id)aValue] 指定した値より大きい値であるか
[[subject should] beBetween:(id)aLowerEndpoint and:(id)anUpperEndpoint] 指定した2つの値の間の値であるか
[[subject should] beInTheIntervalFrom:(id)aLowerEndpoint to:(id)anUpperEndpoint] 指定した2つの値の間の値であるか
[[subject should] beTrue] TRUEであるか
[[subject should] beYes] YESであるか
[[subject should] beZero] 0であるか

オブジェクトのテスト

 オブジェクトをテストするマッチャーには、クラスを比較するマッチャーや、セレクタに対応しているかテストするマッチャーなどがあります。

マッチャー 説明
[[subject should] beKindOfClass:(Class)aClass] 指定したクラスであるか
[[subject should] beMemberOfClass:(Class)aClass] 指定したクラスまたはサブクラスであるか
[[subject should] conformToProtocol:(Protocol *)aProtocol] 指定したプロトコルに準拠しているか
[[subject should] respondToSelector:(SEL)aSelector] 指定したセレクタに対応しているか

配列のテスト

 配列をテストするマッチャーには、アイテムの数を比較したり、特定のオブジェクトが含まれているかテストするマッチャーなどがあります。

マッチャー 説明
[[subject should] beEmpty] 空であるか
[[subject should] contain:(id)anObject] 指定したオブジェクトが含まれているか
[[subject should] containObjectsInArray:(NSArray *)anArray] 指定した配列内に含まれているか
[[subject should] haveCountOf:(NSUInteger)aCount] 指定したアイテム数であるか
[[subject should] haveCountOfAtLeast:(NSUInteger)aCount] 指定したアイテム数より小さいか
[[subject should] haveCountOfAtMost:(NSUInteger)aCount] 指定したアイテム数より大きいか
[[[subject should] have:(NSUInteger)aCount] collectionKey] 指定したアイテム数であるか
[[[subject should] haveAtLeast:(NSUInteger)aCount] collectionKey] 指定したアイテム数より小さいか
[[[subject should] haveAtMost:(NSUInteger)aCount] collectionKey] 指定したアイテム数より大きいか

 [[subject should] haveCountOf:(NSUInteger)aCount]と[[[subject should] have:(NSUInteger)aCount] collectionKey]はよく似ていますが、前者は配列のオブジェクトを対象とした場合のマッチャーで、後者はオブジェクトのプロパティを対象としたマッチャーです。

メソッドの呼び出しのテスト

 メソッドの呼び出しをテストするマッチャーも、使う機会が多いマッチャーの1つです。メソッドが呼ばれたかどうか確認するマッチャーが基本ですが、これに加えて「何回呼ばれたか」「戻り値はどのような値か」といった条件を付け加えることができます。

マッチャー 説明
[[subject should] receive:(SEL)aSelector] メソッドが呼ばれたか
[[subject should] receive:(SEL)aSelector withCount:(NSUInteger)aCount] メソッドが指定した回数呼ばれたか
[[subject should] receive:(SEL)aSelector withCountAtLeast:(NSUInteger)aCount] メソッドが指定した回数より少ない回数呼ばれたか
[[subject should] receive:(SEL)aSelector withCountAtMost:(NSUInteger)aCount] メソッドが指定した回数より多い回数呼ばれたか
[[subject should] receive:(SEL)aSelector andReturn:(id)aValue] メソッドが呼ばれ、戻り値が指定した値であるか
[[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCount:(NSUInteger)aCount] メソッドが指定した回数呼ばれ、戻り値が指定した値であるか
[[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount] メソッドが指定した回数より少ない回数呼ばれ、戻り値が指定した値であるか
[[subject should] receive:(SEL)aSelector andReturn:(id)aValue withCountAtMost:(NSUInteger)aCount] メソッドが指定した回数より多い回数呼ばれ、戻り値が指定した値であるか

非同期処理のテスト

 Kiwiで非同期処理をテストするには、非同期処理専用のexpectFutureValueメソッドとshouldEventuallyマクロ(またはshouldEventuallyBeforeTimingOutAfterマクロなど)を組み合わせて使います。

 次の例では、NSURLSessionによる非同期処理の結果を比較しています。

// 非同期処理の結果データ
__block NSData *result;
// 非同期処理
NSURL* url = [NSURL URLWithString:@"http://www.atmarkit.co.jp/"];
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask* task =
[session dataTaskWithURL:url
       completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
           // 結果データに代入
           result = data;
       }];
[task resume];
// 非同期処理の1秒後に比較
[[expectFutureValue(result) shouldEventually] beNonNil];

 shouldEventuallyマクロは呼び出されてから1秒後に値を返すようになっています。これによって、非同期処理の完了を1秒待機してから比較できます。待機する秒数をカスタマイズしたい場合はshouldEventuallyBeforeTimingOutAfterマクロを使います。このマクロの引数に待機したい秒数を渡すことで、任意の秒数を待機してから比較できます。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。