【问题标题】:Why do my OCUnit tests fail with "code 138"?为什么我的 OCUnit 测试失败并显示“代码 138”?
【发布时间】:2009-07-09 11:21:00
【问题描述】:

我目前正在尝试使用 XCode 3.1 学习 Objective-c。我一直在开发一个小程序,并决定为其添加单元测试。

我按照 Apple 开发者页面 - Automated Unit Testing with Xcode 3 and 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

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

【问题讨论】:

  • 仅供参考,我在测试中将 BOOL 记录为字符串时遇到了同样的错误:BOOL b = YES; NSLog(@"%@", b);请注意,如果 b = NO,它不会崩溃!

标签: objective-c unit-testing xcode memory-management ocunit


【解决方案1】:

我自己也遇到过很多次,而且总是很烦人。基本上,这通常意味着您的单元测试确实崩溃,但无助于隔离错误。如果单元测试在崩溃之前产生了输出(打开构建 > 构建结果),您通常至少可以了解问题发生时正在运行的测试,但这通常并没有太大帮助。

追踪原因的最佳一般建议是调试您的单元测试。不幸的是,在使用 OCUnit 时,这比选择 Run > Debug 更复杂。但是,您正在使用的同一个教程在底部附近有一个标题为“Using the Debugger with OCUnit”的部分,它解释了如何在 Xcode 中创建自定义可执行文件以执行单元测试的方式调试器可以附加到。当你这样做时,调试器将在发生错误的地方停止,而不是在一切都陷入困境时得到神秘的“代码 138”。

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

  • 永远不要在 init 方法中自动释放 self — 它违反了保留释放内存规则。 如果对象被意外释放,仅此一项就会导致崩溃。例如,在您的 testInitByIndex 方法中,testCard 自动释放 - 因此,最后一行的 [testCard release] == 保证崩溃。
  • 我建议将您的 initByIndex: 方法重命名为 initWithIndex:,或者甚至切换到 initWithSuit:(int)suit rank:(int)rank,这样您就可以传递两个值,而不是单个 int(或 NSUInteger,这将消除测试
  • 如果你真的想要一个返回自动释放对象的方法,你也可以创建一个方便的方法,比如+(Card*)cardWithSuit:(int)suit rank:(int)rank。此方法只会返回一行 alloc/init/autorelease 组合的结果。
  • (次要)一旦你完成了调试,去掉只调用 super 的dealloc。如果您要查找从未释放的内存,无论如何使用 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

【讨论】:

  • 自动释放是罪魁祸首。我错过了输入写问题的文件名,所以提示 4 不是问题。提示 2 - 我的代码确实包含其他初始化函数,包括建议的函数。我想尽可能地限制我的代码以尝试隔离错误。
  • 很高兴有帮助。删除了文件命名提示,因为它是一个错字。你很聪明,只发布了导致错误的代码。 :-)
猜你喜欢
  • 1970-01-01
  • 2018-12-17
  • 1970-01-01
  • 1970-01-01
  • 2021-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
相关资源
最近更新 更多