2010-03-04 2 views
0

Mon problème est que je me fais une erreur:Comment utiliser mock et vérifier les méthodes de OCMock dans l'objectif-C?

OCMckObject[NSNumberFormatter]: expected method was not invoked:setAllowsFloats:YES

J'ai écrit le code suivant:

(void) testReturnStringFromNumber 
{ 
    id mockFormatter = [OCMockObject mockForClass:[NSNumberFormatter class]]; 
    StringNumber *testObject = [[StringNumber alloc] init]; 

    [[mockFormatter expect] setAllowsFloats:YES]; 
    [testObject returnStringFromNumber:80.23456]; 
    [mockFormatter verify]; 
} 


@implementation StringNumber 

- (NSString *) returnStringFromNumber:(float)num 
{ 
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 
    [formatter setAllowsFloats:YES]; 

    NSString *str= [formatter stringFromNumber:[NSNumber numberWithFloat:num]]; 

    [formatter release]; 
    return str; 
} 

@end 

Répondre

4

Parce que votre implémentation StringNumber utilise son propre objet NSNumberFormatter, pas celui que vous avez créé dans votre test Cas. Vous devez effectuer une opération de refactoring classique appelée "inversion de dépendance", où votre objet StringNumber prend son formateur en tant que paramètre ou ivar au lieu de le créer en interne. Ensuite, vous pouvez passer votre simulateur de formatage dans le cadre de votre test.

+0

Merci Graham. Je l'ai fait et ça marche maintenant. Mais, je veux savoir 2 est-il un moyen de tester les objets internes à l'intérieur d'une fonction? Et, s'il vous plaît dites-moi, y at-il un bon tutoriel avec des exemples sur OCMock avec iPhone est disponible sur Internet? J'ai beaucoup cherché, mais j'ai trouvé très peu, presque aucun. S'il vous plaît aider. Merci – san

+0

@san: Vous ne pouvez pas faire cela. Vous pouvez tester le comportement global de la fonction/méthode. Si vous avez besoin de grains plus fins, votre API est trop grossière et vous devez interrompre vos méthodes ou paramétrer les objets avec lesquels elles travaillent. –

0

Vous pouvez essayer de surcharger dynamiquement la méthode init de NSNumberFormatter afin de renvoyer votre objet fantaisie. Dans vos tests, vous devez créer un ivar à partir de id mockFormatter. Ainsi, votre méthode de test devient:

(void) testReturnStringFromNumber 
{ 
    mockFormatter = [OCMockObject mockForClass:[NSNumberFormatter class]]; 
    StringNumber *testObject = [[StringNumber alloc] init]; 

    [[mockFormatter expect] setAllowsFloats:YES]; 
    [testObject returnStringFromNumber:80.23456]; 
    [mockFormatter verify]; 
} 

Ensuite, vous devez ajouter une catégorie pour la classe NSNumberFormatter

@implementation NSNumberFormatter (mock) 

- (id)init { 
    id object; 

    if (mockFormatter) { 
     object = mockFormatter 
    } else { 
     object = invokeSupersequent(); 

    return object; 
} 

@end 

Ainsi, lorsque la méthode d'initialisation de NSNumberFormatter sera appelée dans votre code sous test, un objet simulé sera utilisé si vous l'avez défini.

Sources: - http://cocoawithlove.com/2009/12/sample-mac-application-with-complete.html

Cordialement, Quentin

0

Une autre idée: Vous pouvez essayer de faire une Ivar avec formatter en classe StringNumber. Ensuite, il sera possible de définir la valeur de formatage en utilisant le codage de valeur de clé de vos tests.

Cordialement, Quentin

1

Pour l'exemple de code que vous présentez, je ne vous inquiétez pas pour se moquer NSNumberFormatter. Vous essayez de vérifier que [StringNumber returnStringFromNumber:] renvoie une chaîne appropriée. Votre test ne devrait pas se soucier dans ce cas quelle méthode il utilise pour calculer cette chaîne, juste que votre méthode fonctionne comme prévu.

Puisque vous venez de retourner la valeur retournée par numberWithFloat:, et NSNumberFormatter est une classe utilitaire, vous ne devriez pas le mocker. Ensuite, si le comportement de numberWithFloat: change dans le futur, votre test échouera et vous pourrez décider de changer le test ou de changer votre implémentation.

Si vous moquez NSNumberFormatter, vous êtes essentiellement juste tester que votre méthode retourne quel que soit il retourne, donc si son comportement change, le comportement de votre classe changera aussi, un vous ne saurez pas (à temps de test).

J'ai tendance à utiliser des simulacres à:

    facteur
  • une dépendance qui dépend d'un environnement particulier/configuration/données Coeff
  • une dépendance qui ne fonctionne pas au moment de l'essai
  • vérifier le comportement de ma classe pour différents cas de comportement du code dépendant (par exemple, lorsque la dépendance renvoie zéro, lorsque la dépendance renvoie une valeur négative, quand la dépendance renvoie une exception, etc.)
Questions connexes