HTTPクライアント通信のテストをするにはOHHTTPStubsという便利なスタブツールライブラリがありますが、現在POSTメソッドで送るBodyのテストを簡単にすることははできません。
(AFNetworkingとNSURLProtocolを使って擬似的な方法で行う方法はwikiに書いてあります。 https://github.com/AliSoftware/OHHTTPStubs/wiki/Testing-for-the-request-body-in-your-stubs )
以下はNSURLSessionの特定の使い方をしている場合に限定した、OCMockを使って簡単にテストする方法のメモです。
テスト対象
テスト対象コードはこんな感じ
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:WEB_API_URL]];
request.HTTPMethod = @"POST";
request.HTTPBody = requestBodyData;
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.TLSMaximumSupportedProtocol = kTLSProtocol12;
sessionConfig.TLSMinimumSupportedProtocol = kTLSProtocol1;
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
__weak typeof(self) wself = self;
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
if (!wself) { return; }
// 完了時の処理
}] resume];
Stub作成
乱暴ですが、NSURLSessionとNSURLSessionTaskをモックして、dataTaskWithRequest:completionHandler:とresumuをstub化するメソッドcreateNSURLSessoinStub()を用意します。
// モックオブジェクトを返すメソッド
// 引数はrequestのチェック用ブロックと、レスポンスを返すメソッド
// レスポンスはOHHTTPStubsResponseを利用
id createNSURLSessoinStub(
BOOL (^requestCheck)(NSURLRequest *request),
OHHTTPStubsResponse* (^responseCreator)(NSURLRequest *request, NSData* requestBody))
{
id urlSession = OCMClassMock([NSURLSession class]);
// クラスメソッドの置き換え
OCMStub([urlSession sessionWithConfiguration:OCMOCK_ANY
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]])
.andReturn(urlSession);
OCMStub([urlSession dataTaskWithRequest:[OCMArg checkWithBlock:requestCheck]
completionHandler:OCMOCK_ANY])
.andDo(^(NSInvocation *invocation)
{
__unsafe_unretained NSURLRequest *request;
__unsafe_unretained void (^completionHandler)(NSData *data, NSURLResponse *response, NSError *error);
[invocation getArgument:&request atIndex:2];
[invocation getArgument:&completionHandler atIndex:3];
__block id urlSessionTask = OCMClassMock([NSURLSessionDataTask class]);
OCMStub([urlSessionTask resume])
.andDo(^(NSInvocation *invocation)
{
NSData *requestBody = request.HTTPBody;
OHHTTPStubsResponse* stubResponse = responseCreator(request, requestBody);
NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL
statusCode:stubResponse.statusCode
HTTPVersion:@"HTTP/1.1"
headerFields:stubResponse.httpHeaders];
NSData *body = nil;
if (stubResponse.dataSize > 0) {
NSMutableData *bodyBuffer = [NSMutableData dataWithLength:stubResponse.dataSize];
[stubResponse.inputStream open];
NSInteger readBytes = [stubResponse.inputStream read:bodyBuffer.mutableBytes
maxLength:bodyBuffer.length];
[stubResponse.inputStream close];
if (readBytes > 0) {
body = bodyBuffer;
}
}
completionHandler(body?:[NSData data], response, stubResponse.error);
});
[invocation setReturnValue:&urlSessionTask];
});
return urlSession;
}
これでOHHTTPStubsのstubRequestsPassingTest:withStubResponse:と同じイメージで使えます。
使い方
使い方の例
// Json用にラップするヘルパーメソッド
id createNSURLSessoinStubWithBodyJson(
BOOL (^requestCheck)(NSURLRequest *request),
OHHTTPStubsResponse* (^responseCreator)(NSURLRequest *request, NSDictionary* requestBodyJson))
{
return createNSURLSessoinStub(requestCheck,
^OHHTTPStubsResponse*(NSURLRequest *request, NSData* requestBody) {
NSError *error = nil;
id obj = [NSJSONSerialization JSONObjectWithData:requestBody options:0 error:&error];
if (!obj || error != nil) {
return [OHHTTPStubsResponse responseWithData:[@"fake server error" dataUsingEncoding:NSASCIIStringEncoding]
statusCode:500
headers:@{}];
}
return responseCreator(request, obj);
});
}
// テストの実体
- (void)testHogeHogeApi {
// stubの準備
createNSURLSessoinStubWithBodyJson(
^BOOL(NSURLRequest *request) {
// リクエストのチェック
return [request.HTTPMethod isEqualToString:@"POST"]
&& [request.URL.path hasSuffix:@"/hogehoge"];
},^OHHTTPStubsResponse*(NSURLRequest *request, NSDictionary* requestJson) {
// ここでリクエストボディをチェック
if (![requestJson[@"id"] isEqual:@(1)] ||
![requestJson[@"hogetype"] isEqual:@"fuga"]) {
return [OHHTTPStubsResponse responseWithData:nil statusCode:500 headers:nil];
}
// レスポンスを返す
return [OHHTTPStubsResponse responseWithJSONObject:@{@"time":@"2006-01-02T15:04:05Z07:00"}
statusCode:200
headers:@{}];
});
// テストの実行
}
注意点
NSURLSessionの使い方が変わるとうまく動かなくなります。
テスト対象の実装を限定するようなテストはスジが悪いと思われますので、よく考えてお使いください。