OCMock を GHUnit と組み合わせて使用し、Graham Lee の「BrowseOverflow」プロジェクトを Test-Driven iOS Development から再作成しようとしています。
私の理解では、テストしている現在のクラス以外のオブジェクトをモックすることです。私は、いくつかの機能のために Answer クラスに依存している Question クラスを使用しています。
Question.h
@class Answer;
@interface Question : NSObject {
NSMutableSet *answerSet;
}
@property (retain) NSDate *date;
@property NSInteger score;
@property (copy) NSString *title;
@property (readonly) NSArray *answers;
- (void)addAnswer:(Answer *)answer;
@end
Question.m
#import "Question.h"
#import "Answer.h"
@implementation Question
@synthesize date;
@synthesize score;
@synthesize title;
- (id)init
{
if ((self = [super init])) {
answerSet = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addAnswer:(Answer *)answer
{
[answerSet addObject:answer];
}
- (NSArray *)answers
{
return [[answerSet allObjects] sortedArrayUsingSelector:@selector(compare:)];
}
@end
Answer.h
#import <Foundation/Foundation.h>
@class Person;
@interface Answer : NSObject
@property (readwrite) NSString *text;
@property (readwrite) Person *person;
@property (readwrite) NSInteger score;
@property (readwrite, getter = isAccepted) BOOL accepted;
- (NSComparisonResult)compare:(Answer *)otherAnswer;
@end
Answer.m
#import "Answer.h"
@implementation Answer
@synthesize text;
@synthesize person;
@synthesize score;
@synthesize accepted;
- (NSComparisonResult)compare:(Answer *)otherAnswer
{
if (accepted && !(otherAnswer.accepted)) {
return NSOrderedAscending;
} else if (!accepted && otherAnswer.accepted) {
return NSOrderedDescending;
}
if (score > otherAnswer.score) {
return NSOrderedAscending;
} else if (score < otherAnswer.score) {
return NSOrderedDescending;
}
return NSOrderedSame;
}
@end
テスト中に OCMock を使用して Answer のインスタンスをサブインしようとしましたが、約 10% の時間しか機能しませんでした。
- (void)testHighScoreAnswerBeforeLow
{
lowScore = [OCMockObject mockForClass:[Answer class]];
[[[lowScore stub] andReturn:OCMOCK_VALUE((NSInteger) {4})] score];
[question addAnswer:lowScore];
highScore = [OCMockObject mockForClass:[Answer class]];
[[[highScore stub] andReturn:OCMOCK_VALUE((NSInteger) {-4})] score];
[question addAnswer:highScore];
[[lowScore expect] compare:[OCMArg any]];
NSArray *answers = question.answers;
NSInteger highIndex = [answers indexOfObject:highScore];
NSInteger lowIndex = [answers indexOfObject:lowScore];
GHAssertTrue(highIndex < lowIndex, @"High-scoring index comes first");
[highScore verify];
[lowScore verify];
}
問題は、NSSet がオブジェクトをメモリに格納する方法 (ややランダム) と、OCMock が余分なメソッドが呼び出されていないことを確認するという事実によって引き起こされる競合だと思います。この特定のテストから OCMock を除外することで解決しましたが、これは悪いテストのようです。
- (void)testHighScoreAnswerBeforeLow
{
lowScore = [[Answer alloc] init];
lowScore.score = -4;
[question addAnswer:lowScore];
highScore = [[Answer alloc] init];
highScore.score = 4;
[question addAnswer:highScore];
NSArray *answers = question.answers;
NSInteger highIndex = [answers indexOfObject:highScore];
NSInteger lowIndex = [answers indexOfObject:lowScore];
GHAssertTrue(highIndex < lowIndex, @"High-scoring index comes first");
}
並べ替えのアルゴリズムが何回比較する必要があるかわからない場合に、OCMock を並べ替えアルゴリズムとうまく連携させることは可能ですか? そうでない場合、これは OCMock を使用することの欠点ですか、それとも構造化されていないコードですか?