views:

39

answers:

1

I Have a Class that needs to make an HTTP request to a server in order to get some information. For example:

- (NSUInteger)newsCount {
    NSHTTPURLResponse *response;
    NSError *error;
    NSURLRequest *request = ISKBuildRequestWithURL(ISKDesktopURL, ISKGet, cookie, nil, nil);
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    if (!data) {
        NSLog(@"The user's(%@) news count could not be obtained:%@", username, [error description]);
        return 0;
    }
    NSString *regExp = @"Usted tiene ([0-9]*) noticias? no leídas?";
    NSString *stringData = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
    NSArray *match = [stringData captureComponentsMatchedByRegex:regExp];
    [stringData release];
    if ([match count] < 2)
        return 0;
    return [[match objectAtIndex:1] intValue];
}

The things is that I'm unit testing (using OCUnit) the hole framework but the problem is that I need to simulate/fake what the NSURLConnection is responding in order to test different scenarios and because I can't relay on the server to test my framework.

So the question is Which is the best ways to do this?

A: 

It's always tricky to test methods that call class methods like NSURLConnection sendSynchronousRequest

Here are a couple of options:

a) Use Matt Gallagher's invokeSupersequent macro to intercept the call. Your unit test would contain code like this:

@implementation NSURLConneciton (UnitTests)

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error {
    if (someFlagYourTestUsesToInterceptTheCall) {        
        // return test NSData instance      
    }   
    return invokeSupersequent(request, &response, &error);
}

@end

Then you set someFlagYourTestUsesToInterceptTheCall to force it to intercept the call and return your test data.

b) Another alternative is to move that call into its own method in your class under test:

-(NSData *)retrieveNewsCount:(NSURLRequest *)request {
    NSHTTPURLResponse *response;
    NSError *error;
    return [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

Then intercept that call in your test case using OCMock:

-(void)testNewsCount {
    // instantiate your class
    id myObject = ...;
    id mock = [OCMockObject partialMockForObject:myObject];
    [[[mock stub] andCall:@selector(mockNewsCount:) onObject:self] retrieveNewsCount:[OCMArg any]];
    NSUInteger count = [myObject newsCount];
    // validate response
    ...
}

// in the same test class:
-(NSData *)mockNewsCount:(NSURLRequest *)request {
    // return your mock data
    return mockData;
}

In this case, OCMock's stub:andCall:onObject:someMethod intercepts just this call to your object's method in order to inject some test data at test time.

chrispix