2009-07-09 24 views
10

Actualmente estoy tratando de aprender objetivo-c usando XCode 3.1. He estado trabajando en un pequeño programa y decidí agregarle pruebas unitarias.¿Por qué mis pruebas OCUnit fallan con el "código 138"?

Seguí los pasos en la página del desarrollador de Apple - Automated Unit Testing with Xcode 3 and Objective-C. Cuando agregué mi primera prueba, funcionó bien cuando las pruebas fallaron, pero cuando corregí las pruebas la compilación falló. Xcode el siguiente error:

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

intentar aislar mi error, me re-siguieron los pasos del ejemplo de ensayo Unidad arriba y el ejemplo práctico. Cuando agregué una versión simplificada de mi código y un caso de prueba, el error volvió.

Este es el código que he creado:

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 me dieron el mismo error registrar una BOOL como una cadena en mi prueba: BOOL b = SÍ; NSLog (@ "% @", b); Tenga en cuenta que si b = NO, ¡no se bloquea! – Rob

Respuesta

15

que he encontrado en numerosas ocasiones a mí mismo, y siempre es molesto. Básicamente, generalmente significa que su unidad prueba hizo bloqueo, pero no ayuda a aislar el error. Si la unidad prueba la salida producida antes de bloquearse (abra Crear> Generar resultados) generalmente puede tener al menos una idea de qué prueba se estaba ejecutando cuando ocurrió el problema, pero esto por sí solo no suele ser demasiado útil.

La mejor sugerencia general para rastrear la causa es depurar las pruebas de su unidad. Cuando se usa OCUnit, lamentablemente es más complejo que seleccionar Ejecutar> Depurar. Sin embargo, el mismo tutorial que está utilizando tiene una sección cerca de la parte inferior titulada "Usando el depurador con OCUnit" que explica cómo crear un ejecutable personalizado en Xcode para ejecutar las pruebas unitarias de forma que el depurador pueda adjuntar. Cuando lo haga, el depurador se detendrá donde ocurrió el error, en lugar de obtener el misterioso "código 138" cuando todo se incendia.

Aunque puede que no sea capaz de adivinar exactamente lo que está causando el error, tengo algunas sugerencias ...

  • nunca, nunca AutoRelease self en un método init - Viola memoria liberación retener- reglas. Eso solo dará lugar a bloqueos si el objeto se lanza inesperadamente. Por ejemplo, en su método testInitByIndex, testCard vuelve a lanzarse automáticamente, por lo tanto, [testCard release] en la última línea == bloqueo garantizado.
  • me gustaría sugerir el cambio de nombre de su método initByIndex: a initWithIndex:, o incluso cambiar a initWithSuit:(int)suit rank:(int)rank lo que puede pasar ambos valores, en lugar de una sola int (o un NSUInteger, lo que eliminaría las pruebas de < 0) que usted tiene que manejar.
  • Si realmente quiere un método que devuelve un objeto liberado automáticamente, también puede crear un método de conveniencia como +(Card*)cardWithSuit:(int)suit rank:(int)rank en su lugar. Este método simplemente devolvería el resultado de una combinación alloc/init/autorelease de una sola línea.
  • (Menor) Una vez que haya terminado de depurar, deshacerse de la dealloc que acaba de llamar a super. Si está tratando de encontrar memoria que nunca se desasigne, es mucho más fácil de encontrar usando Instrumentos de todos modos.
  • (Niggle) Para su método de prueba, considere usar STAssetEquals(testCard.rank, 0, ...) en su lugar. Prueba lo mismo, pero cualquier error resultante es un poco más fácil de entender.
  • (Trivial) No tiene que declarar los métodos de prueba de la unidad en el @interface. OCUnit ejecuta dinámicamente cualquier método del formato -(void)test... para usted. No está de más declararlos, pero te ahorrarás algo de mecanografía si simplemente los omites. En una nota relacionada, generalmente tengo solo un archivo .m para pruebas unitarias y coloco la sección @interface en la parte superior de ese archivo. Esto funciona muy bien ya que nadie más necesita incluir mi interfaz de prueba de unidad.
  • (Simplicity) A menos que haya subclase CardTestCases, es más simple eliminar el archivo .h y colocar la @interface en la parte superior del archivo .m. Los archivos de encabezado son necesarios cuando varios archivos deben incluir las declaraciones, pero esto generalmente no es el caso con las pruebas unitarias.

Esto es lo que el archivo de prueba podría ser similar con estas sugerencias:

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

autorrelease fue el culpable. Extraño escribir los nombres de los archivos escribiendo la pregunta, así que la sugerencia 4 no fue un problema. consejo 2: mi código contiene otras funciones init, incluida la sugerida. Quería limitar mi código tanto como fuera posible en un intento de aislar el error. – Joe

+0

Me alegro de que haya ayudado. Se eliminó la sugerencia de nomenclatura del archivo porque era un error tipográfico. Es inteligente haber publicado solo el código que causó el error. :-) –

Cuestiones relacionadas