2009-07-09 6 views
10

Ich versuche derzeit, objective-c mit XCode 3.1 zu lernen. Ich habe an einem kleinen Programm gearbeitet und beschlossen, Unit-Tests hinzuzufügen.Warum schlagen meine OCUnit-Tests mit "Code 138" fehl?

Ich folgte den Schritten auf der Apple Developer-Seite - Automated Unit Testing with Xcode 3 and Objective-C. Als ich meinen ersten Test hinzufügte, funktionierte es gut, als die Tests fehlschlugen, aber als ich die Tests korrigierte, scheiterte der Build. Xcode den folgenden Fehler gemeldet:

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

Versuch, meine Fehler zu isolieren, habe ich die Schritte von der Unit-Test Beispiel erneut gefolgt oben und das Beispiel arbeitete. Als ich eine vereinfachte Version meines Codes und einen Testfall hinzugefügt habe, wurde der Fehler zurückgegeben. Hier

ist der Code, den ich erstellt:

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

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 habe ich den gleichen Fehler ein BOOL als String in meinem Test-Anmeldung: BOOL b = JA; NSLog (@ "% @", b); Beachten Sie, dass wenn b = NEIN, es nicht abstürzt! – Rob

Antwort

15

Ich habe dies viele Male selbst begegnet, und es ist immer ärgerlich. Grundsätzlich bedeutet es normalerweise, dass Ihr Gerät abstürzt, aber nicht hilft, den Fehler zu isolieren. Wenn die Einheit die Ausgabe vor dem Absturz testet (Öffnen Build> Build Results), können Sie sich zumindest ein Bild davon machen, welcher Test ausgeführt wurde, als das Problem auftrat, aber das alleine ist normalerweise nicht sehr hilfreich.

Der beste allgemeine Vorschlag zum Aufspüren der Ursache ist debug your Unit Tests. Bei Verwendung von OCUnit ist dies leider komplexer als die Auswahl von Run> Debug. Dasselbe Tutorial, das Sie verwenden, hat jedoch einen Abschnitt namens "Verwenden des Debuggers mit OCUnit", in dem erläutert wird, wie eine benutzerdefinierte ausführbare Datei in Xcode erstellt wird, um die Komponententests so auszuführen, dass der Debugger eine Verbindung herstellen kann. Wenn Sie dies tun, wird der Debugger anhalten, wo der Fehler aufgetreten ist, statt den mysteriösen "Code 138" zu erhalten, wenn alles in Flammen aufgeht.

Obwohl ich nicht in der Lage sein kann, genau zu erraten, was den Fehler verursacht, kann ich ein paar Vorschläge ...

  • NIEMALS self in einer Init-Methode Autorelease - es verletzt behalten Release Speicher Regeln. Das allein führt zu Abstürzen, wenn das Objekt unerwartet freigegeben wird. Zum Beispiel, in Ihrer 10 Methode, testCard kommt wieder freigegeben - daher [testCard release] auf der letzten Zeile == garantiert Absturz.
  • Ich würde Ihre initByIndex: Methode initWithIndex: oder auch die Umstellung auf initWithSuit:(int)suit rank:(int)rank so können Sie beide Werte übergeben, statt eines einzigen int (oder einem NSUInteger, die für < 0 Tests beseitigen würde) empfiehlt die Umbenennung, die Sie behandeln müssen.
  • Wenn Sie wirklich wollen eine Methode, die ein automatisch freigegebenes Objekt zurückgibt, können Sie auch eine bequeme Methode wie +(Card*)cardWithSuit:(int)suit rank:(int)rank stattdessen erstellen. Diese Methode würde nur das Ergebnis einer einzeiligen alloc/init/autorelease-Kombination zurückgeben.
  • (Minor) Sobald Sie mit dem Debuggen fertig sind, loswerden die dealloc, die nur zu Super ruft. Wenn Sie versuchen, Speicher zu finden, der nie freigegeben wurde, ist es viel einfacher, Instrumente zu verwenden.
  • (Niggle) Verwenden Sie für Ihre Testmethode stattdessen STAssetEquals(testCard.rank, 0, ...). Es testet die gleiche Sache, aber jeder resultierende Fehler ist ein bisschen einfacher zu verstehen.
  • (Trivial) Sie müssen keine Einheitentestmethoden im @interface deklarieren. OCUnit führt dynamisch jede Methode des Formats -(void)test... für Sie aus. Es tut nicht weh, sie zu deklarieren, aber Sie sparen sich ein wenig Tipparbeit, wenn Sie sie einfach weglassen. Zu diesem Zweck habe ich normalerweise nur eine .m-Datei für Komponententests und stelle den Abschnitt @interface an die Spitze dieser Datei. Das funktioniert großartig, da niemand sonst meine Komponententestschnittstelle mit einbeziehen muss.
  • (Einfachheit) Wenn Sie nicht die Unterklasse CardTestCases ableiten, ist es einfacher, einfach die .h-Datei zu entfernen und die @ Schnittstelle stattdessen an die Spitze der .m-Datei zu setzen. Header-Dateien sind notwendig, wenn mehrere Dateien die Deklarationen enthalten müssen. Dies ist bei Unit-Tests normalerweise nicht der Fall. Hier

ist, was die Testdatei wie mit diesen Vorschlägen aussehen könnte:

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

Autorelease war der Schuldige. Ich habe die Dateinamen beim Schreiben der Frage nicht eingegeben, daher war Tipp 4 kein Problem. Tipp 2 - Mein Code enthält andere Init-Funktionen einschließlich der vorgeschlagenen. Ich wollte meinen Code so weit wie möglich begrenzen, um den Fehler zu isolieren. – Joe

+0

Froh, dass geholfen hat. Der Dateinamenstipp wurde entfernt, da es sich um einen Tippfehler handelt. Sie sind klug, nur den Code gepostet zu haben, der den Fehler verursacht hat. :-) –

Verwandte Themen