iOSのアプリケーション開発で、ネットワーク関連の処理をユニットテストする際にはOHHTTPStubsを使うことが多いと思います。
この記事では、試したことやハマったことを備忘録的に書いていきます。
注意点
バックグラウンドで動作する機能のUTには使えない
バックグラウンドで動作する通信は、以下の理由でスタブすることができません。
* カスタマイズされたNSURLProtocol
を使うことができない
* バックグラウンド転送はiOSがハンドリングしている
アップロードには対応していない
NSURLProtocolClient
は、データが送信されたということを検知する手段がないため、アップロード関連の処理をスタブすることができません。
そのため、NSURLRequest
のhttpBody
や-[NSURLSession uploadTaskWithRequest:fromData:]
などのデータを送っても、何も起こりません。
また、-URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
も呼び出されません。
NSURLSessionと組み合わせる時の注意点
こちらの記事に書かれている通り、NSURLSession
を使ってPOST
のリクエストを送るときに、NSURLRequest
のhttpBody
にアクセスすることができません。
この現象はAppleのバグによるものです……。
そのため、以下のようにhttpBody
に埋め込まれたパラメータを元にスタブしたレスポンスの内容を変えたい、という処理が書きづらくなっています。
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:@"127.0.0.1"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
NSData *data = request.httpBody;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSDictionary *responseHeader = @{@"Content-Type":@"application/json"};
if (dict[@"key"] == @"hoge") {
return [OHHTTPStubsResponse responseWithData:succeedResponseData statusCode:200 headers:responseHeader];
} else {
return [OHHTTPStubsResponse responseWithData:failedResponseData statusCode:200 headers:responseHeader];
}
}];
対策として、以下のような選択肢が挙げられると思います。
-
こちらの記事に書かれている、
AFNetworking
を使う回避策を試してみる。 -
gamakoさんの記事を参考に、
NSURLSession
とNSURLSessionTask
をモックしてみる。 - 厳しい現実を見つめなおす。回避策は諦め、テスト対象コードをもう少しテストしやすい設計になるよう見なおしてみる。(個別のリクエスト送信ごとにテストできる設計にするとか)
(この現象は、iOS8.1で確認した現象です。これ以降のiOSでは改善されているかもしれませんが……手元で確認してみましょう。)
[OHHTTPStubs removeAllStubs]
はレスポンスの処理が終わってから実行しましょう
[OHHTTPStubs removeAllStubs]
は、必ずOHHTTPStubs
のレスポンスの処理が完了してから呼び出しましょう。
非同期なテストケースでレスポンスが完了していない場合に[OHHTTPStubs removeAllStubs]
を実行すると、
正常にスタブを解除できないため、別なテストケースで使うときに失敗してしまう場合もあります。
なので、この記事にも書かれている通り、レスポンスの処理が終わってからテストを終了する、という形に書きましょう。
さもないと、「tearDown
で正常に解除しているのに!」という羽目に陥りかねません。