2009-07-09 8 views
10

J'essaie actuellement d'apprendre l'objectif-c en utilisant XCode 3.1. J'ai travaillé sur un petit programme et j'ai décidé d'y ajouter des tests unitaires. J'ai suivi les étapes de la page Apple Developer - Automated Unit Testing with Xcode 3 and Objective-C. Quand j'ai ajouté mon premier test, ça a bien fonctionné quand les tests ont échoué, mais quand j'ai corrigé les tests, la compilation a échoué. Xcode signalé l'erreur suivante:Pourquoi mes tests OCUnit échouent-ils avec le "code 138"?

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

Essayer d'isoler mon erreur, je re-suivre les étapes de l'exemple de test unitaire ci-dessus et l'exemple travaillé. Lorsque j'ai ajouté une version simplifiée de mon code et un scénario de test, l'erreur est retournée.

Voici le code que j'ai créé:

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

Pour votre information j'ai eu la même erreur l'enregistrement d'une BOOL comme une chaîne dans mon test: BOOL b = OUI; NSLog (@ "% @", b); Notez que si b = NO, il ne plante pas! – Rob

Répondre

15

J'ai ce à plusieurs reprises rencontré moi-même, et il est toujours ennuyeux. Fondamentalement, cela signifie généralement que vos tests unitaires ont fait crash, mais n'aident pas à isoler l'erreur. Si les tests unitaires ont produit des résultats avant le crash (Open Build> Build Results), vous pouvez au moins avoir une idée du test en cours lorsque le problème est survenu, mais cela n'est généralement pas très utile.

La meilleure suggestion générale pour rechercher la cause est de déboguer vos tests unitaires. Lorsque vous utilisez OCUnit, cela est malheureusement plus complexe que de sélectionner Exécuter> Déboguer. Cependant, le même didacticiel que vous utilisez comporte une section intitulée «Utilisation du débogueur avec OCUnit» qui explique comment créer un exécutable personnalisé dans Xcode pour exécuter vos tests unitaires de manière à ce que le débogueur puisse s'y attacher. Lorsque vous le faites, le débogueur s'arrêtera là où l'erreur s'est produite, au lieu d'obtenir le mystérieux "code 138" quand tout tombe en flammes.

Bien que je ne sois pas capable de deviner exactement ce qui cause l'erreur, j'ai quelques suggestions ...

  • JAMAIS, JAMAIS autorelease self dans une méthode d'initialisation - il constitue une violation mémoire à libération conserver règles. Cela seul conduira à des plantages si l'objet est libéré de manière inattendue. Par exemple, dans votre méthode testInitByIndex, testCard revient autoeleased - par conséquent, [testCard release] sur la dernière ligne == crash garanti.
  • Je vous suggère de renommer votre méthode initByIndex:-initWithIndex:, ou même changer à initWithSuit:(int)suit rank:(int)rank afin que vous puissiez passer les deux valeurs, au lieu d'un seul int (ou un NSUInteger, ce qui éliminerait les tests pour < 0) que vous devez gérer.
  • Si vous vraiment voulez une méthode qui renvoie un objet autoreleased, vous pouvez également créer une méthode pratique comme +(Card*)cardWithSuit:(int)suit rank:(int)rank à la place. Cette méthode retournerait simplement le résultat d'une combinaison alloc/init/autorelease d'une ligne.
  • (Mineur) Une fois que vous avez terminé le débogage, débarrassez-vous du dealloc qui appelle simplement super. Si vous essayez de trouver de la mémoire qui n'est jamais libérée, il est beaucoup plus facile de trouver en utilisant des instruments de toute façon.
  • (Niggle) Pour votre méthode de test, utilisez plutôt STAssetEquals(testCard.rank, 0, ...). Il teste la même chose, mais toute erreur qui en résulte est un peu plus facile à comprendre.
  • (trivial) Vous n'avez pas besoin de déclarer les méthodes de test unitaires dans le @interface. OCUnit exécute dynamiquement n'importe quelle méthode du format -(void)test... pour vous. Cela ne fait pas de mal de les déclarer, mais vous vous éviterez de taper si vous les omettez. Sur une note connexe, j'ai généralement seulement un fichier .m pour les tests unitaires, et mettre la section @interface en haut de ce fichier. Cela fonctionne très bien car personne d'autre n'a besoin d'inclure mon interface de test.
  • (Simplicité) Sauf si vous sous-classez CardTestCases, il est plus simple d'éliminer simplement le fichier .h et de placer à la place l'interface @ en haut du fichier .m. Les fichiers d'en-tête sont nécessaires lorsque plusieurs fichiers doivent inclure les déclarations, mais ce n'est généralement pas le cas avec les tests unitaires.

Voici ce que le fichier de test pourrait ressembler à ces suggestions:

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 était le coupable. J'ai manqué tapé les noms de fichier écrivant la question ainsi le conseil 4 n'était pas un problème. Astuce 2 - Mon code contient d'autres fonctions d'initialisation, y compris celle suggérée. Je voulais limiter mon code autant que possible pour tenter d'isoler l'erreur. – Joe

+0

Heureux qui a aidé. Suppression de l'astuce de dénomination de fichier car il s'agissait d'une faute de frappe. Vous êtes intelligent d'avoir posté seulement le code qui a provoqué l'erreur. :-) –

Questions connexes