ご注意!!!この記事の情報は古くなっています。XCode6以降では XCTestExpectationを使う のが良いでしょう。
XCTestを用いて非同期処理のテストを書く場合、完了フラグを操作してwhile loopで回す方法がよく紹介されています。
// テスト実行完了フラグ
__block BOOL isFinished = NO;
[SNSUser loginWithBlock^(SNSUser* user, NSError* error) {
XCTAssertNotNil(user, @"hoge");
XCTAssertNil(error, @"fuga");
// Blocksの中のテストが終わったら完了フラグをYESに
isFinished = YES;
}];
// 完了フラグがYESになるまで待機
while (!isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
XCTAssertTrue(isFinished, @"");
これはこれで便利なのですが、XCTestAsyncという便利そうなユーティリティを見つけたので紹介。
インストール
CocoaPodsから行うのが簡単です。
pod "XCTestAsync"
テストクラスへの導入
ヘッダファイルをインポート。
#import <XCTestAsync/XCTestAsync.h>
テストメソッドのsuffixをAsyncとして記述します。こちらはとあるプロジェクトのログイン認証処理のテストで、スタブサーバに対してリクエストを投げて成功することを期待しています。
// suffixにAsyncをつける
- (void)testLoginSuccessAsync
{
Auth *auth = [Auth sharedInstance];
[auth loginWithEmail:@"user@example.com"
password:@"password"
successBlock:^(AuthCredential *credential) {
// successBlock内で結果の確認
XCTAssertTrue([credential.accessToken isEqualToString:@"foobar"], @"Check access token");
XCTAssertTrue([credential.refreshToken isEqualToString:@"barbar"], @"Check refresh token");
XCTAssertEqualObjects(credential.expiresIn, @123, @"Check expires");
// 非同期処理のテストが完了したいことを通知する
XCAsyncSuccess();
} failureBlock:^(NSError *error) {
}];
// テストメソッド全体のタイムアウト設定
XCAsyncFailAfter(10, @"");
}
このテストを実行するとXCAyncFailAfter()
まで一気に実行されますが、引数で指定された秒数待機します。大気中にXCAsyncSuccess()
が呼ばれるとテストは成功とみなされて完了します。
このフレームワークの良いと感じたところは、特別な書き方が不要でXCTest風にテストを呼び出すことができる事と、Xcodeからも通常通りテストを走らせられる点(当たり前っちゃ当たり前)です。
NLTHTTPStubServerと組み合わせてテストを書いているのですが、良い感触なのでしばらく使ってみようと思います。