Skip to content

Testing for the request body in your stubs

AliSoftware edited this page Jun 14, 2014 · 26 revisions

The issue with HTTPBody

As seen in issue #52, unfortunately when you send POST requests (with a body) using NSURLSession, once the request arrives to [OHHTTPStubs stubRequestsPassingTest:withStubResponse:, the HTTPBody or the NSURLRequest can't be accessed anymore (probably already transformed internally to an HTTPBodyStream?).

Therefore, you cannot directly test the request.HTTPBody in your [OHHTTPStubs stubRequestsPassingTest:withStubResponse:] as the HTTPBody will be nil at that time.

The workaround

A nice workaround if you really need to check for the body is to use [NSURLProtocol setProperty:forKey:inRequest:] which allows you to associate arbitrary key/value pairs to your NSURLRequests that you can later query using [NSURLProtocol propertyForKey:inRequest:].

Thus you can easily use this method to store the request body, or the parameters dictionary used to build the query part of your GET URLs or the body of your POST request, or the RPC method that your request intends to call, or whatnot, alongside your request. And use it to conditionally stub your requests then.

Example using AFNetworking 2.0+

If you are using AFNetworking 2.0+ to build your request, you can provide a custom AFHTTPRequestSerializer class to associate some properties to your requests when building them.

1. Create a new AFHTTPRequestSerializer subclass

@interface AnnotatedRequestSerializer : AFHTTPRequestSerializer @end
@implementation AnnotatedRequestSerializer
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(NSDictionary *)parameters
                                     error:(NSError * __autoreleasing *)error
{
    NSMutableURLRequest* req = [super requestWithMethod:method URLString:URLString parameters:parameters error:error];
    [NSURLProtocol setProperty:parameters forKey:@"parameters" inRequest:req];
    [NSURLProtocol setProperty:req.HTTPBody forKey:@"HTTPBody" inRequest:req];
    return req;
}
@end

2. Use it with your AFHTTPSessionManager

When you initialize your AFHTTPSessionManager instance, simply set its requestSerializer property to an instance of this AnnotatedRequestSerializer:

sessionMgr = [[AFHTTPSessionManager alloc] initWithBaseURL:… sessionConfiguration:…];
sessionMgr.requestSerializer = [AnnotatedRequestSerializer serializer];

This way, every request build using [sessionMgr POST:… parameters:parameters success:… failure:…] will automatically have those two properties stored alongside with it containing the parameters and body.

3. Send your request using AFNetworking

[sessionMgr POST:@"/login" parameters:@{ @"login":@"foo", @"password":@"bar" } success:… failure:… ];

### 4. Write your stubs

Finally, you could write your stubs that way to test the parameters used for the request:

[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest request) { NSDictionary requestParams = [NSURLProtocol propertyForKey:@"parameters" inRequest:request]; // You could also query the request body if needed using the associated property too: // NSData* requestBody = [NSURLProtocol propertyForKey:@"HTTPBody" inRequest:request]; // NSString* bodyString = [[NSString alloc] initWithData:requestBody encoding:NSUTF8StringEncoding];

    // Only stub POST requests to "/login" with "user" = "foo" & "password" = "bar"
    return [request.HTTPMethod isEqualToString:@"POST"]
        && [request.URL.path isEqualToString:@"/login"]
        && [requestParams[@"user"] isEqualToString:@"foo"]
        && [requestParams[@"password"] isEqualToString:@"bar"];
} withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
    return [OHHTTPStubsResponse responseWithData:validLoginResponseData statusCode:200 headers:headers];
}].name = @"login";