2009-07-09 4 views
10

Attualmente sto cercando di imparare Objective-C utilizzando XCode 3.1. Ho lavorato su un piccolo programma e ho deciso di aggiungere test delle unità.Perché i miei test OCUnit falliscono con "codice 138"?

ho seguito le istruzioni riportate nella pagina di Apple Developer - Automated Unit Testing with Xcode 3 and Objective-C. Quando ho aggiunto il mio primo test, ha funzionato correttamente quando i test hanno avuto esito negativo, ma quando ho corretto i test la build non è riuscita. Xcode ha riportato il seguente errore:

error: Test host '/Users/joe/Desktop/OCT/build/Debug/OCT.app/Contents/MacOS/OCT' exited abnormally with code 138 (it may have crashed).

cercando di isolare il mio errore, ho ri-seguito i passi da esempio Unità prova di cui sopra e l'esempio funzionato. Quando ho aggiunto una versione semplificata del mio codice e un caso di test, l'errore è stato restituito.

ecco il codice che ho creato:

Card.h

#import <Cocoa/Cocoa.h> 
#import "CardConstants.h" 

@interface Card : NSObject { 
    int rank; 
    int suit; 
    BOOL wild ; 
} 

@property int rank; 
@property int suit; 
@property BOOL wild; 

- (id) initByIndex:(int) i; 

@end 

Card.m

#import "Card.h" 

@implementation Card 

@synthesize rank; 
@synthesize suit; 
@synthesize wild; 

- (id) init { 
    if (self = [super init]) { 
     rank = JOKER; 
     suit = JOKER; 
     wild = false; 
    } 
    return [self autorelease]; 
} 

- (id) initByIndex:(int) i { 
    if (self = [super init]) { 
     if (i > 51 || i < 0) { 
      rank = suit = JOKER; 
     } else { 
      rank = i % 13; 
      suit = i/13; 
     } 
     wild = false; 
    } 
    return [self autorelease]; 
} 

- (void) dealloc { 
    NSLog(@"Deallocing card"); 
    [super dealloc]; 
} 

@end 

CardTestCases.h

#import <SenTestingKit/SenTestingKit.h> 

@interface CardTestCases : SenTestCase { 
} 
- (void) testInitByIndex; 
@end 

CardTestCases.m

#import "CardTestCases.h" 
#import "Card.h" 

@implementation CardTestCases 

- (void) testInitByIndex { 
    Card *testCard = [[Card alloc] initByIndex:13]; 
    STAssertNotNil(testCard, @"Card not created successfully"); 
    STAssertTrue(testCard.rank == 0, 
       @"Expected Rank:%d Created Rank:%d", 0, testCard.rank); 
    [testCard release]; 
} 
@end 
+0

FYI ho ottenuto lo stesso errore di registrazione di un BOOL come una stringa nel mio test: BOOL b = SÌ; NSLog (@ "% @", b); Nota che se b = NO, non si blocca! – Rob

risposta

15

ho incontrato questo numerose volte me stesso, ed è sempre fastidioso. In sostanza, in genere significa che l'unità test fatto incidente, ma non aiuta isolare l'errore. Se l'unità verifica l'output prima di bloccarsi (apri Build> Build Results) di solito puoi almeno avere un'idea di quale test era in esecuzione quando si è verificato il problema, ma questo di solito non è di grande aiuto.

Il miglior suggerimento generale per rintracciare la causa è il eseguire il debug dell'unità test. Quando si utilizza OCUnit, questo è sfortunatamente più complesso di selezionare Esegui> Debug. Tuttavia, lo stesso tutorial che stai utilizzando ha una sezione in fondo intitolata "Utilizzo del debugger con OCUnit" che spiega come creare un eseguibile personalizzato in Xcode per eseguire i test di unità in un modo a cui il debugger può collegarsi. Quando lo fai, il debugger si ferma dove si è verificato l'errore, invece di ottenere il misterioso "codice 138" quando tutto va in fiamme.

Anche se non può essere in grado di indovinare esattamente che cosa sta causando l'errore, io ho alcuni suggerimenti ...

  • MAI, MAI autorelease self in un metodo init - viola memoria rilascio trattenere- regole. Questo da solo causerà arresti anomali se l'oggetto viene rilasciato in modo imprevisto. Ad esempio, nel metodo testInitByIndex, testCard torna autoreleased - quindi, [testCard release] sull'ultima riga == incidente garantito.
  • Io suggerirei di rinominare il metodo initByIndex:-initWithIndex:, o anche il passaggio a initWithSuit:(int)suit rank:(int)rank in modo da poter passare entrambi i valori, invece di un singolo int (o un NSUInteger, che eliminerebbe il test per < 0) che si deve gestire.
  • Se si in realtà desidera un metodo che restituisce un oggetto autoreleased, è anche possibile creare un metodo di convenienza come +(Card*)cardWithSuit:(int)suit rank:(int)rank invece. Questo metodo restituirebbe solo il risultato di una combinazione alloc/init/autorelease di una riga.
  • (Minore) Una volta terminato il debug, rimuovere lo dealloc che chiama semplicemente a super. Se stai cercando di trovare memoria che non è mai stata rilasciata, è molto più facile da trovare utilizzando gli strumenti comunque.
  • (Niggle) Per il metodo di prova, prendere in considerazione l'utilizzo di STAssetEquals(testCard.rank, 0, ...). Mette alla prova la stessa cosa, ma ogni errore risultante è un po 'più facile da capire.
  • (triviale) Non è necessario dichiarare i metodi di test unitario nello @interface. OCUnit esegue dinamicamente qualsiasi metodo del formato -(void)test... per te. Non fa male a dichiararli, ma ti risparmi un po 'di digitazione se li ometti. In una nota correlata, di solito ho solo un file .m per i test unitari e metto la sezione @interface nella parte superiore di quel file. Funziona benissimo poiché nessun altro ha bisogno di includere la mia interfaccia di test di unità.
  • (Semplicità) Salvo la sottoclasse CardTestCases, è più semplice eliminare il file .h e inserire invece l'interfaccia @ nella parte superiore del file .m. I file di intestazione sono necessari quando più file devono includere le dichiarazioni, ma questo di solito non è il caso dei test unitari.

Ecco ciò che il file di prova potrebbe essere simile con questi suggerimenti:

CardTest.m

#import <SenTestingKit/SenTestingKit.h> 
#import "Card.h" 

@interface CardTest : SenTestCase 
@end 

@implementation CardTest 

- (void) testInitWithIndex { 
    Card *testCard = [[Card alloc] initWithIndex:13]; 
    STAssertNotNil(testCard, @"Card not created successfully"); 
    STAssertEquals(testCard.rank, 0, @"Unexpected card rank"); 
    [testCard release]; 
} 
@end 
+0

il colpevole è stato il risarcimento. Mi mancava digitare i nomi dei file per scrivere la domanda, quindi il suggerimento 4 non era un problema. suggerimento 2 - il mio codice contiene altre funzioni di init incluso quello suggerito. Volevo limitare il più possibile il mio codice nel tentativo di isolare l'errore. – Joe

+0

Felice che ha aiutato. Rimosso il suggerimento di denominazione del file poiché era un refuso. Sei intelligente di aver pubblicato solo il codice che ha causato l'errore. :-) –