Quelle est la meilleure façon de tester des méthodes protégées et privées dans Ruby, en utilisant le Test::Unit
framework Ruby standard ?
Je suis sûr que quelqu'un dira dogmatiquement que "vous ne devriez tester que des méthodes publiques unitaires; si cela nécessite des tests unitaires, ce ne devrait pas être une méthode protégée ou privée", mais je ne suis pas vraiment intéressé à en débattre. J'ai plusieurs méthodes qui sont protégées ou privées pour de bonnes et valables raisons, ces méthodes privées / protégées sont modérément complexes, et les méthodes publiques de la classe dépendent du fonctionnement correct de ces méthodes protégées / privées, j'ai donc besoin d'un moyen de tester les méthodes protégées / privées.
Encore une chose ... Je mets généralement toutes les méthodes pour une classe donnée dans un fichier, et les tests unitaires pour cette classe dans un autre fichier. Idéalement, j'aimerais que toute la magie implémente cette fonctionnalité de "test unitaire des méthodes protégées et privées" dans le fichier de test unitaire, pas dans le fichier source principal, afin de garder le fichier source principal aussi simple et direct que possible.
la source
Réponses:
Vous pouvez contourner l'encapsulation avec la méthode d'envoi:
C'est une «fonctionnalité» de Ruby. :)
Il y a eu un débat interne pendant le développement de Ruby 1.9 qui envisageait de
send
respecter la vie privée et de l'send!
ignorer, mais au final, rien n'a changé dans Ruby 1.9. Ignorez les commentaires ci-dessous pour discutersend!
et casser des choses.la source
send!
chose, il a été révoqué il y a longtemps,send/__send__
peut appeler des méthodes de toute visibilité - redmine.ruby-lang.org/repositories/revision/1?rev=13824public_send
(documentation ici ) si vous voulez respecter la confidentialité. Je pense que c'est nouveau dans Ruby 1.9.Voici un moyen simple si vous utilisez RSpec:
la source
Rouvrez simplement la classe dans votre fichier de test et redéfinissez la ou les méthodes comme publiques. Vous n'avez pas à redéfinir les tripes de la méthode elle-même, il suffit de passer le symbole dans l'
public
appel.Si votre classe d'origine est définie comme ceci:
Dans votre fichier de test, faites quelque chose comme ceci:
Vous pouvez transmettre plusieurs symboles à
public
si vous souhaitez exposer des méthodes plus privées.la source
instance_eval()
pourrait aider:Vous pouvez l'utiliser pour accéder directement aux méthodes privées et aux variables d'instance.
Vous pouvez également envisager d'utiliser
send()
, ce qui vous donnera également accès à des méthodes privées et protégées (comme James Baker l'a suggéré)Vous pouvez également modifier la métaclasse de votre objet de test pour rendre les méthodes privées / protégées publiques uniquement pour cet objet.
Cela vous permettra d'appeler ces méthodes sans affecter les autres objets de cette classe. Vous pouvez rouvrir la classe dans votre répertoire de test et les rendre publiques pour toutes les instances de votre code de test, mais cela peut affecter votre test de l'interface publique.
la source
Une façon dont je l'ai fait dans le passé est:
la source
Vous pouvez également les refactoriser dans un nouvel objet dans lequel ces méthodes sont publiques et leur déléguer en privé dans la classe d'origine. Cela vous permettra de tester les méthodes sans métarubie magique dans vos spécifications tout en les gardant privées.
Quelles sont ces raisons valables? D'autres langages POO peuvent se passer de méthodes privées du tout (smalltalk vient à l'esprit - là où les méthodes privées n'existent que comme convention).
la source
Semblable à la réponse de @ WillSargent, voici ce que j'ai utilisé dans un
describe
bloc pour le cas particulier du test de certains validateurs protégés sans avoir à passer par le processus lourd de création / mise à jour avec FactoryGirl (et vous pouvez utiliser de laprivate_instance_methods
même manière):la source
Pour rendre publiques toutes les méthodes protégées et privées de la classe décrite, vous pouvez ajouter ce qui suit à votre spec_helper.rb sans avoir à toucher à aucun de vos fichiers de spécifications.
la source
Vous pouvez «rouvrir» la classe et fournir une nouvelle méthode qui délègue à la classe privée:
la source
Je pencherais probablement vers l'utilisation de instance_eval (). Avant de connaître instance_eval (), cependant, je créerais une classe dérivée dans mon fichier de test unitaire. Je définirais alors la ou les méthodes privées comme publiques.
Dans l'exemple ci-dessous, la méthode build_year_range est privée dans la classe PublicationSearch :: ISIQuery. Dériver une nouvelle classe uniquement à des fins de test me permet de définir une ou plusieurs méthodes pour qu'elles soient publiques et, par conséquent, directement testables. De même, la classe dérivée expose une variable d'instance appelée «résultat» qui n'était auparavant pas exposée.
Dans mon test unitaire, j'ai un cas de test qui instancie la classe MockISIQuery et teste directement la méthode build_year_range ().
la source
Dans Test :: Unit, le framework peut écrire,
Ici, "nom_méthode" est une méthode privée.
& en appelant cette méthode peut écrire,
la source
Voici un ajout général à la classe que j'utilise. C'est un peu plus insensé que de rendre publique la méthode que vous testez, mais dans la plupart des cas, cela n'a pas d'importance et c'est beaucoup plus lisible.
L'utilisation d'envoi pour accéder aux méthodes protégées / privées est interrompue dans la version 1.9, ce n'est donc pas une solution recommandée.
la source
Pour corriger la première réponse ci-dessus: dans Ruby 1.9.1, c'est Object # send qui envoie tous les messages, et Object # public_send qui respecte la confidentialité.
la source
Au lieu de obj.send, vous pouvez utiliser une méthode singleton. Il s'agit de 3 lignes de code supplémentaires dans votre classe de test et ne nécessite aucune modification du code réel à tester.
Dans les cas de test, vous utilisez ensuite
my_private_method_publicly
chaque fois que vous souhaitez testermy_private_method
.http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send
pour les méthodes privées a été remplacé parsend!
dans la version 1.9, mais asend!
été supprimé ultérieurement . Fonctionne doncobj.send
parfaitement bien.la source
Je sais que je suis en retard à la fête, mais ne testez pas les méthodes privées ... Je ne vois aucune raison de le faire. Une méthode accessible au public utilise cette méthode privée quelque part, teste la méthode publique et la variété de scénarios qui entraîneraient l'utilisation de cette méthode privée. Quelque chose entre, quelque chose sort. Tester des méthodes privées est un grand non-non, et il sera beaucoup plus difficile de refactoriser votre code plus tard. Ils sont privés pour une raison.
la source
Pour ce faire:
Vous pouvez l'implémenter dans votre fichier test_helper:
la source
Disrespect
enPrivacyViolator
(: P) et fait que ladisrespect_privacy
méthode modifie temporairement la liaison du bloc, afin de rappeler l'objet cible à l'objet wrapper, mais uniquement pour la durée du bloc. De cette façon, vous n'avez pas besoin d'utiliser un paramètre de bloc, vous pouvez simplement continuer à référencer l'objet avec le même nom.