Xcode 6 は、XCTestExpectation
. 非同期プロセスをテストするときは、このプロセスが非同期で完了するという「期待」を確立し、非同期プロセスを発行した後、一定時間期待が満たされるのを待ち、クエリが終了したら非同期で期待に応えます。
例えば:
- (void)testDataTask
{
XCTestExpectation *expectation = [self expectationWithDescription:@"asynchronous request"];
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLSessionTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, @"dataTaskWithURL error %@", error);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode];
XCTAssertEqual(statusCode, 200, @"status code was not 200; was %d", statusCode);
}
XCTAssert(data, @"data nil");
// do additional tests on the contents of the `data` object here, if you want
// when all done, Fulfill the expectation
[expectation fulfill];
}];
[task resume];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
以下の私の以前の回答は、以前XCTestExpectation
のものですが、歴史的な目的のために保持します。
テストはメイン キューで実行されており、リクエストは非同期で実行されているため、テストは完了ブロックのイベントをキャプチャしません。リクエストを同期させるには、セマフォまたはディスパッチ グループを使用する必要があります。
例えば:
- (void)testDataTask
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLSessionTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, @"dataTaskWithURL error %@", error);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode];
XCTAssertEqual(statusCode, 200, @"status code was not 200; was %d", statusCode);
}
XCTAssert(data, @"data nil");
// do additional tests on the contents of the `data` object here, if you want
// when all done, signal the semaphore
dispatch_semaphore_signal(semaphore);
}];
[task resume];
long rc = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 60.0 * NSEC_PER_SEC));
XCTAssertEqual(rc, 0, @"network request timed out");
}
セマフォは、リクエストが完了するまでテストが完了しないことを保証します。
明らかに、上記のテストはランダムな HTTP リクエストを作成しているだけですが、うまくいけばアイデアを示しています。そして、私のさまざまなXCTAssert
ステートメントは、次の 4 種類のエラーを識別します。
NSError
オブジェクトは nil ではありませんでした。
HTTP ステータス コードが 200 ではありませんでした。
NSData
オブジェクトはゼロでした。
完了ブロックが 60 秒以内に完了しませんでした。
おそらく、応答の内容に対するテストも追加することになるでしょう (この簡略化された例では実行しませんでした)。
上記のテストは、メイン キューに何かをディスパッチしている完了ブロックに何もないため、機能することに注意してください。メイン キューを必要とする非同期操作でこれをテストしている場合 (AFNetworking を慎重に使用していないか、自分でメイン キューに手動でディスパッチした場合に発生します)、上記のパターンでデッドロックが発生する可能性があります (ブロックしているため)。ネットワーク要求が終了するのを待っているメイン スレッド)。しかし、の場合NSURLSession
、このパターンはうまく機能します。
シミュレーターとは関係なく、コマンドラインからテストを行うことについて質問されました。いくつかの側面があります。
コマンドラインからテストしたい場合は、コマンドラインxcodebuild
から使用できます。たとえば、コマンド ラインからシミュレーターでテストするには、次のようになります (私の例では、私のスキームは と呼ばれNetworkTest
ます)。
xcodebuild test -scheme NetworkTest -destination 'platform=iOS Simulator,name=iPhone Retina (3.5-inch),OS=7.0'
これにより、スキームが構築され、指定された宛先で実行されます。Xcode 5.1 で、コマンド ラインからシミュレーターでアプリをテストする際に問題が発生するという報告が多数あることに注意してくださいxcodebuild
(上記が正常に動作するマシンが 1 台あるため、この動作を確認できますが、別のマシンではフリーズします)。Xcode 5.1 のシミュレーターに対するコマンド ライン テストは、完全に信頼できるものではないようです。
テストをシミュレーターで実行したくない場合 (これは、コマンド ラインまたは Xcode から実行する場合に適用されます)、MacOS X ターゲットをビルドし、そのビルドに関連付けられたスキームを使用できます。たとえば、Mac OS X ターゲットをアプリに追加し、それを呼び出すスキームを追加しましNetworkTestMacOS
た。
ところで、Mac OS X スキームを既存の iOS プロジェクトに追加する場合、テストがスキームに自動的に追加されない可能性があるため、スキームを編集して手動で追加し、テスト セクションに移動して、テストを追加する必要がある場合があります。クラスがあります。次に、適切なスキームを選択して Xcode からこれらの Mac OS X テストを実行するか、コマンド ラインから実行することもできます。
xcodebuild test -scheme NetworkTestMacOS -destination 'platform=OS X,arch=x86_64'
また、ターゲットを既にビルドしている場合は、適切な DerivedData
フォルダー (この例では~/Library/Developer/Xcode/DerivedData/NetworkTest-xxx/Build/Products/Debug
) に移動xctest
し、コマンド ラインから直接実行することで、これらのテストを直接実行できます。
/Applications/Xcode.app/Contents/Developer/usr/bin/xctest -XCTest すべて NetworkTestMacOSTests.xctest
Xcode セッションからテストを分離する別のオプションは、別の OS X サーバーでテストを行うことです。WWDC 2013 ビデオTesting in XcodeのContinuous Integration and Testingセクションを参照してください。ここに入るには、元の質問の範囲をはるかに超えているため、トピックの紹介を提供するビデオを参照するだけです。
個人的には、Xcode でのテストの統合 (テストのデバッグが非常に簡単になります) がとても気に入っています。また、Mac OS X をターゲットにすることで、そのプロセスでシミュレーターをバイパスできます。しかし、コマンド ライン (または OS X Server) から実行したい場合は、上記の方法が役立つかもしれません。