使用具有未知数量的方法调用的OCMock

时间:2013-05-08 17:16:33

标签: ios objective-c unit-testing tdd ocmock

我正在使用OCMock与GHUnit交界,尝试从测试驱动的iOS开发中重新创建Graham Lee的“BrowseOverflow”项目。

我的理解是模拟任何不是来自您正在测试的当前类的对象。我正在使用我的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在我测试时接收到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的缺点还是结构不良的代码?

1 个答案:

答案 0 :(得分:2)

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

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

您可以使用存根来返回某个值,而不关心它被调用的次数。