2014-04-29 6 views
5

Ich versuche Unit-Test eine Klasse, die AFNEtworking in XCode 5 mit XCTest verwendet. Das Problem, das ich habe, ist, dass die Abschlussblöcke für meine AFHTTPRequestOperation nie ausgeführt werden. Ich nehme an, das ist eine Trennung zwischen XCode, der den Komponententest ausführt, und AFNetworking's Dispatch Queue. Der folgende Testfall besteht, aber die NSLog-Anweisungen in den Completion-Blöcken werden nie erreicht (keine Protokollausgabe und keine in diesen Anweisungen gesetzten Haltepunkte werden abgefangen). Derselbe Code funktioniert außerhalb eines Komponententests. Kann jemand dieses Problem umgehen? Ich benutze Nocilla, um die tatsächlichen Anfragen zu verspotten, das Ergebnis ist das gleiche mit einem echten Server, der gültige Antworten neu einleitet?AFNetworking 2.0 und Komponententest

Edited machen Test nicht und melden Sie sich vars

- (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); 
} 

Konsolenausgabe

 
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). 
+0

Es ruft wahrscheinlich 'waitUntilFinished 'auf, sobald die Hintergrundoperation beendet ist, aber dies wäre bevor die Callbacks aufgerufen werden. Sie könnten versuchen, ['semaphore's] (https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079-CH2 zu verwenden -SW38) bis [warten] (http://goo.gl/lepGmc), bis Sie [signiert] haben (https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference .html # // apple_ref/doc/uid/TP40008079-CH2-SW39) – Rich

+1

Für asynchrone Tests empfehle ich übrigens [Expecta] (https://github.com/specta/expecta/). Sie können dann Sachen wie 'expect ([[req.responseObject objectForKey: @" Erfolg "] boolValue]). Will.beTruthy();' machen (obwohl ich nicht auf die 'beTruthy'-Syntax scharf bin!). Sie müssen nur sicherstellen, dass Sie ein passendes Zeitlimit setzen '[Expecta setAsynchronousTestTimeout: 15]; // Sekunden' – Rich

+0

Dies war nur eine Plausibilitätsprüfung, um sicherzustellen, dass mein tatsächlicher Code dies nicht verursachte. Unter Verwendung des Semaphorbeispiels hier http://stackoverflow.com/questions/20476957/afnetworking-2-waituntilfinished-not-working, hängt es für immer. In beiden Fällen kann ich die Semaphor-Methode in meinem eigentlichen Code nicht verwenden. – jlujan

Antwort

2

Gemäß der Diskussion in den Kommentaren fanden wir, dass waitUntilFinished is once the background operation is complete in Block gesetzt, und es funktioniert nicht warten Sie, bis die Abschlussblöcke aufgerufen wurden.

Es gibt einen viel besseren Rahmen für das asynchrone Testen - Expecta.
Dann statt Aufruf:

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

können Sie tun:

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

Es lots of other matchers sind, so stellen Sie sicher, dass Sie die timeout mit +[Expecta setAsynchronousTestTimeout:] in Ihrem +setUp Verfahren eingestellt.