I’m parsing some json to return a basic string token or error message.
- (void)callBackWithVerifyHttpResponse:(NSData *)response
{
SomeResult *result = [self.parser parseVerifyHttpResponseAndReturnResult:response];
if (result.token) { [self.delegate callBackWithToken:result.token]; }
if (result.error) { [self.delegate callBackWithError:result.error]; }
}
The tests that prove this
- (void)testVerifyCallbackInvokesErrorCallbackOnDelegateWhenParserReturnsError
{
SomeResult *result = [[SomeResult alloc] init];
result.error = @"fail";
[[self.delegate expect] callBackWithError:@"fail"];
[[self.delegate reject] callBackWithToken:OCMArg.any];
[[[self.parser stub] andReturn:result] parseVerifyHttpResponseAndReturnResult:nil];
[self.sut callBackWithVerifyHttpResponse:nil];
[self.delegate verify];
}
- (void)testVerifyCallbackInvokesTokenCallbackOnDelegateWhenParserReturnsToken
{
SomeResult *result = [[SomeResult alloc] init];
result.token = @"token";
[[self.delegate expect] callBackWithToken:@"token"];
[[self.delegate reject] callBackWithError:OCMArg.any];
[[[self.parser stub] andReturn:result] parseVerifyHttpResponseAndReturnResult:nil];
[self.sut callBackWithVerifyHttpResponse:nil];
[self.delegate verify];
}
All was well until I wired this up to an actual endpoint -only to find that unless I modified the callback like the below -it was calling both callbacks (not what I was hoping for)
- (void)callBackWithVerifyHttpResponse:(NSData *)response
{
SomeResult *result = [self.parser parseVerifyHttpResponseAndReturnResult:response];
if (result.token != [NSNull null]) { [self.delegate callBackWithToken:result.token]; }
if (result.error != [NSNull null]) { [self.delegate callBackWithError:result.error]; }
}
So 2 part question
-
why can’t I write a test to prove this? Any time I set the error or token to NULL or NSNull it works fine (yet this code was required for production to work)
-
why would the production code only fail the conditional if I put the
!= [NSNull null](yet I can’t seem to get anything but<null>when I NSLog the values while I run it in the simulator?
Keep in mind the token/error properties look like this on the SomeResult object
@interface SomeResult : NSObject
@property (strong, nonatomic) NSString *token;
@property (strong, nonatomic) NSString *error;
@end
Your original code and tests are expecting either token or error to be
nil, not[NSNull null]. Presumably when you run it against a production endpoint, your parser is setting the value to[NSNull null].These tests should pass with your modified code:
When you’re dealing with a web service and/or a third party parser that could change its behavior subtly, it’s good to code defensively. You could handle both
nilandNSNull:You may also want to make this an if/else–either test for error first, and never set the token if there’s an error, or ignore the error if you get a token. If you don’t want to ever call both callbacks, then make it impossible in your code paths.