views:

317

answers:

2

I'm writing test cases for a wrapper class written around ASIHTTPRequest. For reasons I can't determine, my test cases complete with failure before the ASIHTTPRequest finishes.

Here's how the program flow works.

  1. Start in my test case.
  2. Init my http engine object, instruct it to create a new list
  3. Create the new ASIHTTPRequest object and set it up.
  4. Add the request to an operation queue.
  5. Wait until that queue is empty
  6. Check to see if my delegate methods were called and fail the test if they weren't.

Now, most of the time everything works fine and the test passes, but some of the time it fails because my delegate methods were called AFTER the operation queue returned control to my wait method.

Test Case

// Set my flags to 'NO'
- (void)setUp {
    requestDidFinish = NO;
    requestDidFail = NO;
}

- (void)testCreateList {
    NSString *testList = @"{\"title\": \"This is a list\"}";

    JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
    NSString *requestIdentifier = [engine createList:jsonString];

    [self waitUntilEngineDone:engine];
    NSString *responseString = responseString_;
    [engine release];

    GHAssertNotNil(requestIdentifier, nil);
    GHAssertTrue(requestDidFinish, nil);
    GHAssertTrue([responseString hasPrefix:@"{\"CreateOrEditListResult\""], nil);

}

// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
    [engine waitUntilFinishedRunning]; 
}

// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
    NSLog(@"request did finish");
    requestDidFinish = YES;
    responseIdentifier_ = [requestIdentifier retain];
    responseString_ = [response retain];
}

Engine Code

- (NSString *)createList:(NSString *)list {
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
    [request addRequestHeader:@"Content-Type" value:kContentType];
    [request setRequestMethod:kPOST];
    request.delegate = self;

    [request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];

    NSString *requestIdentifier = [NSString stringWithNewUUID];

    [operationQueue_ addOperation:request];
    [operationDictionary_ setObject:request forKey:requestIdentifier];

    return requestIdentifier;
}

// This is the ASIHTTPRequest delegate method that's called on success
//   but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
    DLog([request responseString]);

    BOOL canNotifiyDelegate = [self.delegate respondsToSelector:@selector(requestFinished:withResponse:)];
    if (canNotifiyDelegate) {
        NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
        NSString *requestIdentifier = [keyArray objectAtIndex:0];
        [operationDictionary_ removeObjectForKey:requestIdentifier];

        if ([keyArray count] != 1) {
            ALog(@"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
        }

        [self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
    }
}

- (void)waitUntilFinishedRunning {
    [operationQueue_ waitUntilAllOperationsAreFinished];
}
+1  A: 

This is the way ASIHTTPRequest works. Delegate methods are called on the main thread, and calls to delegates do not block the request thread, so it's perfectly possible your delegates will be called after the queue finishes.

pokeb
Actually, that's the way i'm expecting it to work. What's throwing me for a loop is that sometimes the delegate methods are called AFTER the queue finishes. If it helps, all tests are run on the main thread.
kubi
Sorry, I had that backwards, what you described is what's happening. Do you know of a reliable way to test whether the request is finished? It sounds like the only way to do it is poll my current object and determine if requestFinished or requestFailed have been called yet.
kubi
A: 

ASIHTTPRequest calls delegate methods on the main thread, by default GH-Unit runs its tests on a background thread. I'm still a little hazy on exactly what was going on, but forcing my network tests to run on the main thread fixed the problem.

I implemented the following method in my network test class.

- (BOOL)shouldRunOnMainThread { 
    return YES;
}
kubi