Почему мои тесты OCUnit перестали работать с “кодом 138”?

7 символов в J: Не уверенный, если это - лучший способ, я несколько плохо знаком с J:)

p=:-:|.

объяснение: |. инвертирует вход.-: выдерживает сравнение. операнды неявны.

p 'radar'
1

p 'moose'
0
10
задан Joe 11 July 2009 в 14:14
поделиться

1 ответ

I've encountered this numerous times myself, and it's always annoying. Basically, it usually means that your unit tests did crash, but doesn't help isolate the error. If the unit tests produced output before crashing (open Build > Build Results) you can usually at least get an idea of what test was running when the problem occurred, but this alone usually isn't too helpful.

The best general suggestion for tracking down the cause is to debug your unit tests. When using OCUnit, this is unfortunately more complex than selecting Run > Debug. However, the same tutorial you're using has a section near the bottom titled "Using the Debugger with OCUnit" which explains how to create a custom executable in Xcode to execute your unit tests in a way that the debugger can attach to. When you do, the debugger will stop where the error occurred, instead of getting the mysterious "code 138" when everything goes down in flames.

Although I may not be able to guess exactly what's causing the error, I do have a few suggestions...

  • NEVER, EVER autorelease self in an init method — it violates retain-release memory rules. That alone will lead to crashes if the object is released unexpectedly. For example, in your testInitByIndex method, testCard comes back autoreleased — therefore, [testCard release] on the last line == guaranteed crash.
  • I'd suggest renaming your initByIndex: method to initWithIndex:, or even switching to initWithSuit:(int)suit rank:(int)rank so you can pass both values, instead of a single int (or an NSUInteger, which would eliminate testing for < 0) that you have to handle.
  • If you really want a method that returns an autoreleased object, you can also create a convenience method like +(Card*)cardWithSuit:(int)suit rank:(int)rank instead. This method would just return the result of a one-line alloc/init/autorelease combination.
  • (Minor) Once you're done debugging, get rid of the dealloc that just calls to super. If you're trying to find memory that's never deallocated, it's much easier to find using Instruments anyway.
  • (Niggle) For your test method, consider using STAssetEquals(testCard.rank, 0, ...) instead. It tests the same thing, but any resulting error is a bit easier to understand.
  • (Trivial) You don't have to declare unit test methods in the @interface. OCUnit dynamically runs any method of the format -(void)test... for you. It doesn't hurt to declare them, but you'll save yourself some typing if you just omit them. On a related note, I usually have only a .m file for unit tests, and put the @interface section at the top of that file. This works great since nobody else needs to include my unit test interface.
  • (Simplicity) Unless you subclass CardTestCases, it is simpler to just eliminate the .h file and put the @interface at the top of the .m file instead. Header files are necessary when multiple files need to include the declarations, but this is usually not the case with unit tests.

Here is what the test file might look like with these 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
15
ответ дан 3 December 2019 в 22:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: