我目前正在尝试使用XCode 3.1学习objective-c。我一直在做一个小程序,并决定添加单元测试。
我按照Apple Developer页面上的步骤进行操作 - 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
答案 0 :(得分:15)
我自己经历过这么多次,而且总是令人讨厌。基本上,它通常意味着您的单元测试发生崩溃,但无助于隔离错误。如果单元测试在崩溃之前生成输出(打开Build&gt; Build Results),通常至少可以了解问题发生时正在运行的测试,但仅此一点通常没有太大帮助。
追踪原因的最佳一般建议是调试您的单元测试。使用OCUnit时,遗憾的是比选择Run&gt;更复杂。调试。但是,您正在使用的同一个教程的底部附近标题为“使用带有OCUnit的调试器”,该部分解释了如何在Xcode中创建自定义可执行文件以执行单元测试调试器可以附加到。当你这样做时,调试器将停止发生错误的地方,而不是在一切都发生火灾时得到神秘的“代码138”。
虽然我可能无法准确猜出导致错误的原因,但我确实有一些建议......
self
- 它违反了保留释放内存规则。如果对象意外释放,那将导致崩溃。例如,在testInitByIndex
方法中,testCard
会自动释放 - 因此,[testCard release]
在最后一行==保证崩溃。initByIndex:
方法重命名为initWithIndex:
,或者切换为initWithSuit:(int)suit rank:(int)rank
,这样您就可以传递这两个值,而不是一个int
(或者NSUInteger
,这将消除您必须处理的&lt; 0)的测试。+(Card*)cardWithSuit:(int)suit rank:(int)rank
。此方法只返回单行alloc / init / autorelease组合的结果。dealloc
。如果您正在尝试查找从未取消分配的内存,那么无论如何都可以更轻松地找到使用仪器。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