为什么我的OCUnit测试失败了"代码138"?

Joe*_*Joe 10 xcode unit-testing memory-management objective-c ocunit

我目前正在尝试使用XCode 3.1学习objective-c.我一直在做一个小程序,并决定添加单元测试.

我按照Apple Developer页面上的步骤 - 使用Xcode 3和Objective-C进行自动单元测试.当我添加我的第一个测试时,它在测试失败时工作正常,但是当我更正测试时,构建失败了.Xcode报告了以下错误:

错误:测试主机'/Users/joe/Desktop/OCT/build/Debug/OCT.app/Contents/MacOS/OCT'异常退出代码138(它可能已崩溃).

试图找出我的错误,我重新按照上面的单元测试示例中的步骤进行操作.当我添加我的代码的简化版本和测试用例时,返回错误.

这是我创建的代码:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

CardTestCases.h

#import <SenTestingKit/SenTestingKit.h>

@interface CardTestCases : SenTestCase {
}
- (void) testInitByIndex;
@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Qui*_*lor 15

我自己曾经遇到过这么多次,而且总是令人讨厌.基本上,它通常意味着您的单元测试确实崩溃,但无助于隔离错误.如果单元测试在崩溃之前生成输出(打开Build> Build Results),通常至少可以了解问题发生时正在运行的测试,但仅此一点通常不太有帮助.

追踪原因的最佳一般建议是调试单元测试.使用OCUnit时,遗憾的是比选择Run> Debug更复杂.但是,您正在使用的相同教程的底部附近标题为" 使用带有OCUnit的调试器 ",该部分解释了如何在Xcode中创建自定义可执行文件,以便以调试器可以附加到的方式执行单元测试.当你这样做时,调试器将停止发生错误的地方,而不是在一切都火上浇油时获得神秘的"代码138".

虽然我可能无法准确猜出导致错误的原因,但我确实有一些建议......

  • 永远不要self在init方法中自动释放- 它违反了保留释放内存规则.如果对象意外释放,那将导致崩溃.例如,在您的testInitByIndex方法中,testCard返回自动释放 - 因此,[testCard release]在最后一行==保证崩溃.
  • 我建议将initByIndex:方法重命名initWithIndex:,甚至切换到,initWithSuit:(int)suit rank:(int)rank这样你就可以传递两个值,而不是一个int(或者一个NSUInteger会消除<0的测试)你必须处理的值.
  • 如果你真的想要一个返回自动释放对象的方法,你也可以创建一个方便的方法+(Card*)cardWithSuit:(int)suit rank:(int)rank.此方法只返回单行alloc/init/autorelease组合的结果.
  • (次要)一旦你完成调试,就摆脱dealloc那些只调用super.如果您正在尝试寻找从未解除分配的内存,那么无论如何使用Instruments都会更容易找到.
  • (Niggle)对于您的测试方法,请考虑使用STAssetEquals(testCard.rank, 0, ...).它测试同样的事情,但任何产生的错误都更容易理解.
  • (普通)您不必在中声明单元测试方法@interface.OCUnit -(void)test...为您动态运行该格式的任何方法.声明它们并没有什么坏处,但如果你省略它们,你会省去一些打字.在相关的说明中,我通常只有一个.m文件用于单元测试,并将@interface部分放在该文件的顶部.这很有效,因为没有其他人需要包含我的单元测试界面.
  • (简单)除非你是子类CardTestCases,否则简单地消除.h文件并将@interface放在.m文件的顶部更简单.当多个文件需要包含声明时,头文件是必需的,但单元测试通常不是这种情况.

以下是这些建议的测试文件的外观:

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
Run Code Online (Sandbox Code Playgroud)