Unit Tests for designs that use notifications
I'm having difficulty testing some logic that uses notifications. I've read about enforcing that particular NSNotifications
are sent, but that doesn't really address the problem I'm seeing.
[SomeObject PerformAsyncOperation]
creates an NSURLRequest
and sets itself as the response delegate. Depending on the content of the response, SomeObject
posts a success or failure NSNotification
to the default NSNotificationCenter
.
The problem with my test is that after PerformAsyncOperation
is called, the test doesn't wait for the response to be sent. Instead, it continues on with the assert - which fails, because the request/response hasn't had time to be sent/received/parsed.
Here is the code:
-(void)testMethod {
SomeObject *x = [[SomeObject alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(success:)
name:SomeObjectSuccess
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(failure:)
name:SomeObjectFailure
object:nil];
operationCompleted = false; // declared in .h
[x PerformAsyncOperation:@"parameter"];
STAssertTrue(operationCompleted , @"Operation has completed.");
[[NSNotificationCenter defaultCenter] removeObserver:self name:SomeObjectSuccess object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:SomeObjectFailure object:nil];
[x release];
}
-(void) failure:(NSNotification *) notification {
operationCompleted = true;
NSLog(@"Received failure message.");
}
-(void) success:(NSNotification *) notification {
operationCompleted = true;
NSLog(@"Received success message.");
}
I've tried sleeping the thread after the call to PerformAsyncOperation
, as well as an empty while (!operationCompleted)
loop. Neither works 开发者_运维问答- sleeping the thread still fails the assert, and the while loop never exits. I've also tried moving the asserts into success:
and failure:
, but because OCUnit expects each method to be a test method, it just executes all three methods immediately and quits (without waiting for the NSURLRequest
's response, which is the whole point of the exercise!).
Any ideas or insight would be greatly appreciated. Thanks!
The problem you face is not notifications, which are synchronous. Rather, it is that you are firing off an asynchronous operation. To make this a repeatable test, you need to resynchronize things.
NSTimeInterval timeout = 2.0; // Number of seconds before giving up
NSTimeInterval idle = 0.01; // Number of seconds to pause within loop
BOOL timedOut = NO;
NSDate *timeoutDate = [[NSDate alloc] initWithTimeIntervalSinceNow:timeout];
while (!timedOut && !operationCompleted)
{
NSDate *tick = [[NSDate alloc] initWithTimeIntervalSinceNow:idle];
[[NSRunLoop currentRunLoop] runUntilDate:tick];
timedOut = ([tick compare:timeoutDate] == NSOrderedDescending);
[tick release];
}
[timeoutDate release];
Beyond this point, you know that either the operation completed, or the test timed out.
For simplicity reasons, and if possible, use the synchronous network requests.
- (void)testExample
{
[self measureBlock:^{
NSURLRequest *request = [[NSURLRequest alloc] initWith...];
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:nil];
XCTAssertEqual(response.statusCode,200);
// process the data from response ... call the delegate methods or whatever
NSObject *object = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
XCTAssertTrue([object isKindOfClass:[NSArray class]]);
}];
}
精彩评论