2014年のWWDCでは、"Testing In Xcode 6"という講演で
XCTestの変更点について、以下の通り説明されています。
- 互換性の向上
- 非同期テスト用APIの追加
- パフォーマンス評価用APIの追加
この記事では、非同期テスト用APIの追加・パフォーマンス評価用APIの追加について説明します。
非同期テスト用APIの追加
XCode6のXCTestでは、非同期テスト用のAPIが追加されました。
バックグラウンドで実行するような処理やネットワークのI/Oなど、非同期な振る舞いをテストするときに使えます。
書き方
非同期な処理を実行するときに、期待する処理が完了したタイミングでXCTestExpectation
のfulfill
を呼び出すよう実装します。
そのときにテストケース側ではwaitForExpectationsWithTimeout:
を呼び出して、期待する処理が完了するか、タイムアウトと判定されるまで処理を中断します。
- (void)testDocumentOpening
{
// 期待した結果が満たされたことを通知するため、XCTestExpectationオブジェクトを生成する。
// このサンプルでは1つだけしか生成していないが、複数のオブジェクトを生成することも可能。
XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:@"UIDocument is opened."];
NSURL *URL = [[NSBundle bundleForClass:[self class]]
URLForResource:@"documentForTesting" withExtension:@"json"];
UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];
[doc openWithCompletionHandler:^(BOOL success) {
XCTAssert(success);
// UIDocumentを開くのに成功したあと、「期待通りに動作した!」ということを示すため、XCTestExpectionのfulfillを実行する
[documentOpenExpectation fulfill];
}];
// タイムアウトするか、すべてのXCTestExpectionオブジェクトのfulfillが呼び出されるまで、RunLoopを回した状態で待機する
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
[doc closeWithCompletionHandler:nil];
}];
}
Success/Failureの判定
すべてのXCTestExpectation
でfulfill
が呼び出された場合、Successと判定されます。
どれか一つでも呼び出されない場合か、タイムアウトしてしまった場合にはFailureと判定されます。
Convinience APIs
タイムアウト以外にも、以下のAPIを使って非同期処理の評価を行うことができます。
// KVOの処理結果を評価するためのAPI
// expectedValueに何らかの値が指定された場合、[NSObject isEqual:]で比較して評価する
// nilの場合、値が変わった最初のタイミングで期待した結果が満たされたと判定する
- (XCTestExpectation *)keyValueObservingExpectationForObject:(id)objectToObserve
keyPath:(NSString *)keyPath
expectedValue:(id)expectedValue;
// KVOの処理結果を評価するためのAPI
// handlerの中で期待した結果が満たされたか評価する。
// nilの場合、値が変わった最初のタイミングで結果が満たされたと判定する
- (XCTestExpectation *)keyValueObservingExpectationForObject:(id)objectToObserve
keyPath:(NSString *)keyPath
handler:(XCKeyValueObservingExpectationHandler)handlerOrNil;
// 指定したKeyのNotificationが通知されるか評価するためのAPI
// handler:がnilの場合は通知されたタイミングで期待した結果が満たされたと判定する
- (XCTestExpectation *)expectationForNotification:(NSString *)notificationName
object:(id)objectToObserve
handler:(XCNotificationExpectationHandler)handlerOrNil;
Further Readings
XCTestCase+AsynchronousTesting.hか、Appleの公式リファレンスがオススメです!
パフォーマンス測定用メソッドの追加
XCode6のXCTestでは、パフォーマンスを評価するためのAPIが追加されました。
機能追加やリファクタリング等、変更を加えたことによって引き起こされたパフォーマンス悪化などを評価するのに使えそうです。
書き方
以下のサンプルで示すmeasureBlock:
や、 measureMetrics:
を使って評価します。
どちらのAPIもブロック内で指定した処理を10回実行し、平均処理速度と、標準偏差を求めます。
- (void)testFileParsePerformanceWithMeasureBlock {
TestTargetClass *target = [TestTargetClass new];
// block内のパフォーマンスを一律で評価したい
[self measureBlock:^{
// ファイルを開く
[target openFile];
// 何かしらファイルの内容をパースする
[target parse];
// ファイルを閉じる
[target closeFile];
}];
}
- (void)testFileParsePerformanceWithMeasureBlock {
TestTargetClass *target = [TestTargetClass new];
// 評価したい処理を細かく指定したい場合、measureMetrics: を使うことができる
[self measureMetrics:[[self class] defaultPerformanceMetrics] automaticallyStartMeasuring:NO
forBlock:^{
// ファイルを開く
[target openFile];
// パース処理でだけ、パフォーマンスを測定する
[self startMeasuring];
[target parse];
[self stopMeasuring];
// ファイルを閉じる
[target closeFile];
}];
}
Success/Failureの判定
以下のどちらかの条件が満たされた場合、テストが失敗したと判定します。
- 平均処理速度が、指定したBaseLineと比較して10%遅くなっている
- 指定したBaseLineより、処理速度のばらつきが大きい(= 標準偏差が大きい)
テスト結果は、上の箇条書きの通り、BaseLineという値を基準にして評価します。
BaseLineは任意のテストの実行結果を指定することもできますし、また、別途編集することもできます。
加えて、CPU等のアーキテクチャ毎に指定することもできるそうです。
なお、BaseLineはxcproj中のxcbaselinesというディレクトリの中に保存されます。
Further Readings
これ以上詳しく知りたければ、XCTestCase.hかAppleのリファレンスがオススメです。