Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AFNetworking 2.0 and Unit Test

I am trying to unit test a class that uses AFNEtworking in XCode 5 using XCTest. The issue I am having is that the completion blocks for my AFHTTPRequestOperation are never being executed. I assume this is some disconnect between XCode running the unit test and AFNetworking's dispatch queue. The following test case passes but the NSLog statements in the completion blocks are never reached (no log output and no breakpoints set on these statements are caught). The same code works outside of a unit test. Does anyone know how to work around this issue? I am using Nocilla to mock the actual requests, the result is the same using a real server retuning valid responses?

Edited to make test fail and log vars set in block

- (void)setUp
{
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.



    [[LSNocilla sharedInstance] start];

    stubRequest(@"POST", @"http://www.example.com/module/api/ping").
    andReturn(200).
    withHeaders(@{@"Content-Type": @"application/json"}).
    withBody(@"{\"success\":true}");

    stubRequest(@"GET", @"http://www.example.com/module/api/ping?testkey=testval").
    andReturn(200).
    withHeaders(@{@"Content-Type": @"application/json"}).
    withBody(@"{\"success\":true}");
}

- (void)tearDown
{
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];

    [[LSNocilla sharedInstance] stop];
    [[LSNocilla sharedInstance] clearStubs];
}

- (void)testSanity
{
    AFSecurityPolicy *policy = [[AFSecurityPolicy alloc] init];
    //[policy setAllowInvalidCertificates:YES];

    AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.example.com/module/api/ping"]];
    //manager.operationQueue = [NSOperationQueue mainQueue];
    [manager setSecurityPolicy:policy];


    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];


    __block id resObj = nil;
    __block id resError = nil;

    AFHTTPRequestOperation *req = [manager POST:@"http://www.example.com/module/api/ping"
                                    parameters:[NSDictionary dictionaryWithObject:@"testval" forKey:@"testkey"]
                                       success:^(AFHTTPRequestOperation *operation, id responseObject) {

                                           NSLog(@"Response: %@", responseObject);
                                           resObj = responseObject;
                                           return;

                                       }
                                       failure:^(AFHTTPRequestOperation *operation, NSError *error) {

                                            NSLog(@"Error: %@", error);
                                           resError = error;
                                           return;
                                       }];
    [req waitUntilFinished];

    NSLog(@"req.status: %d", req.response.statusCode);
    NSLog(@"req.responseObj: %@", req.responseObject);
    XCTAssertTrue(req.isFinished);
    NSLog(@"resObj: %@", resObj);
    NSLog(@"resError: %@", resError);
    XCTAssertEqual([[req.responseObject objectForKey:@"success"] boolValue], YES);

    XCTAssertEqual([[resObj objectForKey:@"success"] boolValue], YES);
}

Console Output

Test Case '-[AppSupportTests testSanity]' started.
2014-04-29 16:45:07.424 xctest[72183:303] req.status: 200
2014-04-29 16:45:07.424 xctest[72183:303] req.responseObj: {
    success = 1;
}
2014-04-29 16:45:07.424 xctest[72183:303] resObj: (null)
2014-04-29 16:45:07.425 xctest[72183:303] resError: (null)
/Users/jlujan/Code/AppSupport/AppSupportTests/AppSupportTests.m:114: error: -[AppSupportTests testSanity] : (([[resObj objectForKey:@"success"] boolValue]) equal to (__objc_yes)) failed: ("NO") is not equal to ("YES")
Test Case '-[AppSupportTests testSanity]' failed (0.003 seconds).
like image 589
jlujan Avatar asked Nov 02 '22 00:11

jlujan


1 Answers

As per the discussion in comments we found that waitUntilFinished is once the background operation is complete, and it does not wait till after the completion blocks have been called.

There's a much better framework for asynchronous testing - Expecta.
Then instead of calling:

XCTAssertTrue(req.isFinished);
XCTAssertEqual([[resObj objectForKey:@"success"] boolValue], YES);

You can do:

expect(req.isFinished).will.beTruthy();
expect([[resObj objectForKey:@"success"] boolValue]).will.beTruthy();

There are lots of other matchers, just make sure you set the timeout with +[Expecta setAsynchronousTestTimeout:] in your +setUp method.

like image 98
Rich Avatar answered Nov 15 '22 06:11

Rich