2014-04-29 28 views
5

Sto provando a testare una classe che utilizza AFNEtworking in XCode 5 usando XCTest. Il problema che sto avendo è che i blocchi di completamento per il mio AFHTTPRequestOperation non vengono mai eseguiti. Presumo che si tratti di una disconnessione tra XCode che esegue il test dell'unità e la coda di invio di AFNetworking. Il seguente test case passa ma le istruzioni NSLog nei blocchi di completamento non vengono mai raggiunte (nessun output di registro e nessun breakpoint impostato su queste istruzioni viene rilevato). Lo stesso codice funziona al di fuori di un test unitario. Qualcuno sa come risolvere questo problema? Sto usando Nocilla per prendere in giro le richieste effettive, il risultato è lo stesso usando un server reale che riattiva le risposte valide?AFNetworking 2.0 e Unit Test

cura per rendere il test di fallire e di log Vars Situato in blocco

uscita
- (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

 
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

Probabilmente chiama 'waitUntilFinished' una volta terminata l'operazione in background, ma prima che vengano richiamati i callback. Potresti provare a utilizzare ['semaforo's] (https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079-CH2 -SW38) a [attendere] (http://goo.gl/lepGmc) fino a quando non si ha [segnalato] (https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference .html # // apple_ref/doc/uid/TP40008079-CH2-SW39) – Rich

+1

Per i test asincroni raccomando [Expecta] (https://github.com/specta/expecta/) a proposito. Puoi quindi fare cose come 'expect ([[req.responseObject objectForKey: @" success "] boolValue]). Will.beTruthy();' (anche se non mi piace la sintassi 'beTruthy'!). Devi solo assicurarti di impostare un timeout appropriato '[Expecta setAsynchronousTestTimeout: 15]; // seconds' – Rich

+0

Questo era solo un controllo di integrità per assicurarmi che il mio codice reale non lo stesse causando. Utilizzando l'esempio del semaforo qui http://stackoverflow.com/questions/20476957/afnetworking-2-waituntilfinished-not-working, si blocca per sempre. Ad ogni modo, non posso usare il metodo semaforo nel mio codice attuale. – jlujan

risposta

2

Come per la discussione nei commenti abbiamo scoperto che waitUntilFinished is once the background operation is complete, e non lo fa aspetta fino a dopo che i blocchi di completamento sono stati chiamati.

C'è un framework molto migliore per i test asincroni - Expecta.
Poi invece di chiamare:

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

si può fare:

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

ci sono lots of other matchers, basta assicurarsi di impostare il timeout con +[Expecta setAsynchronousTestTimeout:] nel metodo +setUp.