开发者

NSOperationQueue and ASIHTTPRequest

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 开发者_JAVA技巧withResponse:[request responseString]];
    }
}

- (void)waitUntilFinishedRunning {
    [operationQueue_ waitUntilAllOperationsAreFinished];
}


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.


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;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜