NSURLConnection デリゲートを単体テストするにはどうすればよいですか? Web からさまざまな ViewController にデータを提供するために、さまざまなプロトコルに準拠する ConnectionDelegate クラスを作成しました。先に進む前に、単体テストの作成を開始したいと思います。しかし、インターネットに接続せずにそれらをユニットとしてテストする方法がわかりません。非同期コールバックを処理するために何をすべきかについても知りたいです。
6 に答える
これはジョンの応答に似ていますが、コメントに収まりませんでした. 最初のステップは、実際の接続を作成していないことを確認することです。これを実現する最も簡単な方法は、接続の作成をファクトリ メソッドにプルし、テストでファクトリ メソッドを置き換えることです。OCMock の部分的なモックのサポートにより、これは次のようになります。
あなたの実際のクラスでは:
- (NSURLConnection *)newAsynchronousRequest:(NSURLRequest *)request
{
return [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
あなたのテストでは:
id objectUnderTest = /* create your object */
id partialMock = [OCMockObject partialMockForObject:objectUnderTest];
NSURLConnection *dummyUrlConnection = [[NSURLConnection alloc]
initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"file:foo"]]
delegate:nil startImmediately:NO];
[[[partialMock stub] andReturn:dummyUrlConnection] newAsynchronousRequest:[OCMArg any]];
これで、テスト対象のオブジェクトが URL 接続を作成しようとすると、実際にはテストで作成されたダミーの接続が取得されます。ダミー接続は有効である必要はありません。開始していないため、使用されることはありません。コードで接続を使用する場合は、NSURLConnection をモックする別のモックを返すことができます。
2 番目のステップは、NSURLConnection の作成をトリガーするオブジェクトのメソッドを呼び出すことです。
[objectUnderTest doRequest];
テスト対象のオブジェクトは実際の接続を使用していないため、テストからデリゲート メソッドを呼び出すことができます。別のモックを使用している NSURLResponse の場合、応答データは、テストの別の場所で定義された文字列から作成されます。
int statusCode = 200;
id responseMock = [OCMockObject mockForClass:[NSHTTPURLResponse class]];
[[[responseMock stub] andReturnValue:OCMOCK_VALUE(statusCode)] statusCode];
[objectUnderTest connection:dummyUrlConnection didReceiveResponse:responseMock];
NSData *responseData = [RESPONSE_TEXT dataUsingEncoding:NSASCIIStringEncoding];
[objectUnderTest connection:dummyUrlConnection didReceiveData:responseData];
[objectUnderTest connectionDidFinishLoading:dummyUrlConnection];
それでおしまい。テスト対象のオブジェクトが接続に対して行うすべての相互作用を効果的に偽装したので、オブジェクトが本来あるべき状態にあるかどうかを確認できるようになりました。
「実際の」コードを見たい場合は、NSURLConnections を使用する CCMenu プロジェクトのクラスのテストを見てください。テストされるクラスも接続という名前であるため、これは少し混乱します。
編集 (2014 年 2 月 18 日): より洗練されたソリューションでこの記事に出くわしました。
http://www.infinite-loop.dk/blog/2011/04/unittesting-asynchronous-network-access/
基本的に、次の方法があります。
- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
} while (!done);
return done;
}
テスト メソッドの最後に、タイムアウトしていないことを確認します。
STAssertTrue([self waitForCompletion:5.0], @"Timeout");
基本フォーマット:
- (void)testAsync
{
// 1. Call method which executes something asynchronously
[obj doAsyncOnSuccess:^(id result) {
STAssertNotNil(result);
done = YES;
}
onError:^(NSError *error) [
STFail();
done = YES;
}
// 2. Determine timeout
STAssertTrue([self waitForCompletion:5.0], @"Timeout");
}
==============
私はパーティーに遅れましたが、非常に簡単な解決策に出会いました。( http://www.cocoabuilder.com/archive/xcode/247124-asynchronous-unit-testing.htmlに感謝します)
.h ファイル:
@property (nonatomic) BOOL isDone;
.m ファイル:
- (void)testAsynchronousMethod
{
// 1. call method which executes something asynchronously.
// 2. let the run loop do its thing and wait until self.isDone == YES
self.isDone = NO;
NSDate *untilDate;
while (!self.isDone)
{
untilDate = [NSDate dateWithTimeIntervalSinceNow:1.0]
[[NSRunLoop currentRunLoop] runUntilDate:untilDate];
NSLog(@"Polling...");
}
// 3. test what you want to test
}
isDone
YES
非同期メソッドが実行されているスレッドで設定されます。
この場合、ステップ 1 で NSURLConnection を作成して開始し、そのデリゲートをこのテスト クラスにしました。の
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
設定しself.isDone = YES;
ました。while ループから抜け出し、テストが実行されます。終わり。
単体テストではネットワーキングを避けます。その代わり:
- メソッド内で NSURLConnection を分離します。
- テスト サブクラスを作成し、そのメソッドをオーバーライドして NSURLConnection のすべてのトレースを削除します。
- 問題のメソッドが必要なときに呼び出されることを確認するためのテストを 1 つ作成します。次に、実際に NSURLConnection を起動することを知っています。
次に、より興味深い部分に集中します。さまざまな特性を持つモック NSURLResponses を合成し、それらを NSURLConnectionDelegate メソッドに渡します。
これを行う私のお気に入りの方法は、NSURLProtocol をサブクラス化し、それをすべての http 要求 (またはその他のプロトコル) に応答させることです。次に、テスト プロトコルを-setup
メソッドに登録し、メソッドで登録解除します-tearDown
。次に、このテスト プロトコルでよく知られたデータをコードに返すようにして、単体テストで検証できるようにします。
このテーマについていくつかのブログ記事を書いています。問題に最も関連するのは、おそらくUsing NSURLProtocol for Injecting Test DataとUnit Testing Asynchronous Network Accessでしょう。
以前の記事で説明した ILCannedURLProtocol も参照してください。ソースはGithubで入手できます。
あなたはこれにチャンスを与えたいかもしれません:
https://github.com/marianoabdala/ZRYAsyncTestCase
単体テスト中の NSURLConnection のサンプル コードを次に示し ます。
これが私がすることです:
- XAMPP コントロール パネルを 入手 http://www.apachefriends.org/en/xampp.html
- Apache サーバーを起動する
- フォルダーに、テスト ファイルを配置します
~/Sites
(必要なデータが何であれ、それを と呼びますmy file.test
)。 - URL http://localhost/~username/myfile.testを使用してデリゲートを開始します
- 使用しない場合は、Apache サーバーを停止します。