2013-10-04 12 views
37
NSMutableArray *arr = [NSMutableArray array]; 
[arr addObject:@"1"]; 
[arr addObject:@"2"]; 
[arr addObject:@"3"]; 

// This statement is fine. 
XCTAssertTrue(arr.count == 3, @"Wrong array size."); 

// This assertion fails with an error: ((arr.count) equal to (3)) failed: ("3") is not equal to ("3") 
XCTAssertEqual(arr.count, 3, @"Wrong array size."); 

Cosa non capisco di XCTAssertEqual? Perché l'ultima asserzione fallisce?Errore XCTAssertEqual: ("3") non è uguale a ("3")

+2

Alcune altre librerie grandi matcher sono: OCHamcrest e Expecta. . c'è anche Kiwi e Cedar: framework di test a pieno titolo con belle librerie di match incorporate. . (Nel caso non li abbia ancora provati). –

risposta

47

Ho anche avuto un po 'di problemi con i test di Xcode 5. Sembra ancora abbastanza buggato con qualche strano comportamento - tuttavia ho trovato la ragione definitiva per cui il tuo particolare non funziona.

Se abbiamo uno sguardo al codice di prova lo vediamo in realtà fa il seguente (preso direttamente da XCTestsAssertionsImpl.h - potrebbe essere più semplice per vedere lì):

#define _XCTPrimitiveAssertEqual(a1, a2, format...) \ 
({ \ 
    @try { \ 
     __typeof__(a1) a1value = (a1); \ 
     __typeof__(a2) a2value = (a2); \ 
     NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ 
     NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ 
     float aNaN = NAN; \ 
     NSValue *aNaNencoded = [NSValue value:&aNaN withObjCType:@encode(__typeof__(aNaN))]; \ 
     if ([a1encoded isEqualToValue:aNaNencoded] || [a2encoded isEqualToValue:aNaNencoded] || ![a1encoded isEqualToValue:a2encoded]) { \ 
       _XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_Equal, 0, @#a1, @#a2, _XCTDescriptionForValue(a1encoded), _XCTDescriptionForValue(a2encoded)),format); \ 
     } \ 
    } \ 
    @catch (id exception) { \ 
     _XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_Equal, 1, @#a1, @#a2, [exception reason]),format); \ 
    }\ 
}) 

Ecco il problema:

Quello che il test sta effettivamente facendo è codificare i valori in un NSValue e poi confrontarli. "Okay," dici, "ma qual è il problema?" Non pensavo che ce ne fosse uno fino a quando non ho fatto il mio caso di prova per questo. Il problema è che lo -isEqualToValue di NSValue deve anche confrontare il tipo di codifica di NSValue e il suo valore effettivo. Entrambi devono essere uguali per il metodo per restituire YES.

Nel tuo caso, arr.count è un NSUInteger che è un typedef di unsigned int. La costante in fase di compilazione 3 degenera presumibilmente in un signed int in fase di runtime. Pertanto, quando i due vengono inseriti in un oggetto NSValue, i loro tipi di codifica non sono uguali e quindi i due non possono essere uguali in base a -[NSValue isEqualToValue].

Puoi dimostrarlo con un esempio personalizzato. Il seguente codice fa esplicitamente esattamente ciò che fa XCTAssertEqual:

// Note explicit types 
unsigned int a1 = 3; 
signed int a2 = 3; 

__typeof__(a1) a1value = (a1); 
__typeof__(a2) a2value = (a2); 

NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; 
NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; 

if (![a1encoded isEqualToValue:a2encoded]) { 
    NSLog(@"3 != 3 :("); 
} 

"3 != 3 :(" apparirà nel registro ogni volta.

Mi affretto ad aggiungere qui che questo è, in effetti, un comportamento previsto. NSValue è supposto per verificare la codifica del tipo durante i confronti. Sfortunatamente non è quello che ci aspettavamo quando testavo due interi ('uguali').

XCTAssertTrue, per inciso, ha una logica molto più diretta e si comporta in genere come previsto (di nuovo, vedere la sorgente effettiva di come determina se l'asserzione non riesce).

+27

Vale la pena notare che la correzione corretta per questo è semplicemente includere le informazioni sul tipo. 'XCTAssertEqual (arr.count, (NSUInteger) 3, @" Dimensione dell'array errato. ");' –

+5

Grazie, un metodo ancora più semplice è: 'XCTAssertEqual (arr.count, 3U, @" Dimensione dell'array errato. ");' – owenfi

+3

Meglio usare '(NSUInteger) 3' piuttosto che' 3U' perché NSUInteger è tipizzato in modo diverso per la compilazione a 64 bit e 32 bit. Per 64 bit, NSUInteger è un 'unsigned long' rispetto a' unsigned int' per 32 bit. – zim

5

Ho avuto anche questo problema. Come @ephemera e @napier hanno indicato, si tratta di un problema di tipo .

Può essere risolto fornendo un valore del tipo corretto, utilizzando i modificatori c-literal.

XCTAssertEqual(arr.count, 3ul, @"Wrong array size."); 

È possibile trovare il tipo corretto, cercando il tipo di ritorno della funzione utilizzata sul lato sinistro - ALT-click su arr.count:

- (NSUInteger)count; 

Ora Alt-clic su NSUInteger per trovare il suo tipo:

typedef unsigned long NSUInteger; 

ora trovare il formato numerico letterale c per unsigned long - Google è un buon amico, ma questa pagina funziona:

http://www.tutorialspoint.com/cprogramming/c_constants.htm

Come un rapido accenno qui, potrebbe essere necessario utilizzare U (unsigned) L (long) o F (float), e assicurarsi che si scrive 1,0 invece di 1 per ottenere un doppio. Anche il minuscolo funziona, come nel mio esempio sopra.

+0

Non penso che funzioni se si desidera che i test funzionino su 32 e 64 bit. L'uso di '3ul' causerà un errore con 32 bit. – ThomasW

0

Un'alternativa è quella di utilizzare solo casting:

XCTAssertEqual(arr.count, (NSUInteger)3, @"Wrong array size."); 

potrebbe essere la soluzione migliore con lo stato attuale degli strumenti, soprattutto se si dispone di codice in cui si sta utilizzando XCTAssertEqual molto e non fare vuoi passare a XCTAssertTrue.

(ho notato @RobNapier fatto questo suggerimento in un commento.)

0

mi sono portato a casa da questo problema, così, molto grato per le soluzioni alternative fornite qui. Quick FYI, sembra che sia stato corretto nella versione Xcode 5.1.

https://developer.apple.com/library/mac/releasenotes/DeveloperTools/RN-Xcode/xc5_release_notes/xc5_release_notes.html

The XCTAssertEqual macro (formerly STAssertEquals using OCUnit) correctly compares scalar values of different types without casting, for example, int and NSInteger. It can no longer accept nonscalar types, such as structs, for comparison. (14435933)

Non ho ancora aggiornato da Xcode 5.0.2 ma il mio collega ha, e le stesse prove XC che in precedenza erano fallendo a causa di questo problema sono ora passa senza la soluzione di colata.

3

Nel caso in cui qualcun altro è alla ricerca di un ruolo guida problema con un doppio confronto tra come me (soluzione di cui sopra non funzionerà per float & doppia), provare:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01); 

Generates a failure when (difference between (\a expression1) and (\a expression2) is > (\a accuracy))).