2013-05-08 66 views
1

我使用OCMock与GHUnit交界处尝试从测试驱动iOS开发重新创建Graham Lee的“BrowseOverflow”项目。使用未知数的方法调用OCMock

我的理解是模拟任何不是来自当前正在测试的类的对象。我正在使用我的Question类,它依赖于我的Answer类来实现某些功能。

Questsion.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在对答案的实例子,当我测试,但它只是工作时间约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的缺点,还是代码结构不好?

回答

2

问题是你正在设定比较的期望。如果你不关心它被调用了多少次,你不应该告诉你期望它被调用一次的模拟。

如果您不关心在模拟对象上调用哪些意外的方法,则可以使用nicemock。

你可以使用存根来返回一个特定的值,而不用关心它被调用的次数。

+1

OCMockito也有很好的嘲笑。 – 2013-05-08 20:49:43

+0

有关存根与嘲讽之间的区别的更多信息,请参阅Martin Fowler的规范文章:http://martinfowler.com/articles/mocksArentStubs.html – tbombach 2013-05-08 20:52:04

+0

谢谢大家的指导!事后看来,这个问题似乎非常基础。 @GrahamLee你会推荐OCMockito而不是OCMock吗? – andrewcopp 2013-05-09 17:47:03

相关问题