2013-05-08 1 views
1

J'utilise OCMock en jonction avec GHUnit pour essayer de recréer le projet "BrowseOverflow" de Graham Lee à partir de Test-Driven iOS Development.Utilisation d'OCMock avec un nombre inconnu d'appels de méthode

Ma compréhension est de se moquer de tout objet qui ne provient pas de la classe en cours que vous testez. Je travaille avec ma classe Question qui dépend de ma classe Answer pour certaines fonctionnalités.

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 

J'essayé d'utiliser OCMock à sous dans les cas de réponse quand je testais, mais il ne fonctionnait environ 10% du temps.

- (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]; 
} 

Je pense que le problème est un conflit provoqué par la façon dont les objets magasins nsset dans la mémoire (un peu au hasard) et le fait que le OCMock vérifie que aucune méthode supplémentaires sont appelés. Je me suis installé en retirant OCMock de ce test particulier mais cela semble être un mauvais test.

- (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"); 
} 

Est-il possible d'obtenir OCMock jouer agréable avec des algorithmes de tri lorsque vous ne savez pas combien de comparaisons dont ils auront besoin de faire? Sinon, est-ce un inconvénient d'utiliser OCMock ou est-ce un code mal structuré?

Répondre

2

Le problème est que vous définissez une attente sur la comparaison. Si vous ne vous souciez pas du nombre de fois que cela s'appelle, vous ne devriez pas dire au simulacre que vous vous attendez à ce qu'il soit appelé une fois.

Vous pouvez utiliser un nicemock si vous ne vous souciez pas des méthodes inattendues qui sont appelées sur les objets fantaisie.

Vous pouvez utiliser un talon pour renvoyer une certaine valeur sans se soucier du nombre de fois qu'il est appelé.

+1

OCMockito a aussi de beaux mocks. –

+0

Pour en savoir plus sur la différence entre les stubs et les mock, regardez l'article canonique de Martin Fowler: http://martinfowler.com/articles/mocksArentStubs.html – tbombach

+0

Merci à tous pour les conseils utiles! Cette question semble vraiment basique avec le recul. @GrahamLee recommanderiez-vous OCMockito au lieu de OCMock? – andrewcopp

Questions connexes