Nous essayons de concevoir notre système de manière à ce qu'il puisse être testé et, dans la plupart des cas, développé à l'aide de TDD. Nous essayons actuellement de résoudre le problème suivant:
À divers endroits, il est nécessaire d’utiliser des méthodes d’assistance statiques telles que ImageIO et URLEncoder (les deux API standard) ainsi que diverses autres bibliothèques composées principalement de méthodes statiques (comme les bibliothèques Apache Commons). Mais il est extrêmement difficile de tester les méthodes qui utilisent de telles classes d'assistance statique.
J'ai plusieurs idées pour résoudre ce problème:
- Utilisez un framework fictif pouvant simuler des classes statiques (comme PowerMock). C'est peut-être la solution la plus simple mais cela donne l'impression d'abandonner.
- Créez des classes wrapper instanciables autour de tous ces utilitaires statiques pour pouvoir les injecter dans les classes qui les utilisent. Cela semble être une solution relativement propre, mais je crains que nous ne finissions par créer énormément de ces classes de wrapper.
- Extrayez chaque appel de ces classes auxiliaires statiques dans une fonction pouvant être remplacée et testez une sous-classe de la classe que je souhaite tester.
Mais je continue de penser que cela doit être un problème auquel de nombreuses personnes doivent faire face lors de la TDD - il doit donc déjà y avoir des solutions à ce problème.
Quelle est la meilleure stratégie pour garder les classes qui utilisent ces aides statiques testables?
la source
Réponses:
(Pas de sources "officielles" ici, je le crains - ce n'est pas comme si on spécifiait comment bien tester. Juste mes opinions, qui seront utiles, espérons-le.)
Lorsque ces méthodes statiques représentent des dépendances authentiques , créez des wrappers. Donc pour des choses comme:
... il est logique de créer une interface.
Mais beaucoup de méthodes dans Apache Commons ne devraient probablement pas être ridiculisées. Par exemple, prenez une méthode pour réunir une liste de chaînes en ajoutant une virgule entre elles. Il est inutile de s'en moquer - laissez simplement l'appel statique faire son travail normal. Vous ne voulez pas ou n'avez pas besoin de remplacer le comportement normal; vous ne traitez pas avec une ressource externe ou quelque chose avec lequel il est difficile de travailler, ce sont juste des données. Le résultat est prévisible et vous ne voudriez jamais que ce soit autre chose que ce que cela vous donnera de toute façon.
Je soupçonne qu'après avoir supprimé tous les appels statiques qui sont vraiment des méthodes pratiques avec des résultats prévisibles et «purs» (tels que l'encodage en base64 ou URL) plutôt que des points d'entrée dans un vaste gâchis de dépendances logiques (comme HTTP), vous constaterez que c'est entièrement pratique de faire la bonne chose avec les dépendances authentiques.
la source
C'est certainement une question / réponse d'opinion, mais pour ce que cela vaut, je pensais mettre mes deux sous. Pour ce qui est de la méthode de style TDD 2, c'est certainement l'approche qui suit à la lettre. L'argument en faveur de la méthode 2 est que si vous avez toujours voulu remplacer l'implémentation de l'une de ces classes - disons une
ImageIO
bibliothèque équivalente -, vous pouvez le faire tout en maintenant la confiance dans les classes qui exploitent ce code.Cependant, comme vous l'avez mentionné, si vous utilisez beaucoup de méthodes statiques, vous finirez par écrire beaucoup de code wrapper. Cela pourrait ne pas être une mauvaise chose à long terme. En termes de maintenabilité, il existe certainement des arguments en ce sens. Personnellement, je préférerais cette approche.
Cela dit, PowerMock existe pour une raison. Il est assez connu que tester des méthodes statiques est très douloureux, d’où le lancement de PowerMock. Je pense que vous devez peser vos options en termes de quantité de travail nécessaire pour emballer toutes vos classes d’aides d’appel par rapport à l’utilisation de PowerMock. Je ne pense pas que ce soit «renoncer» à utiliser PowerMock - je pense simplement que le fait d'encapsuler les classes vous permet plus de flexibilité dans un grand projet. Plus vous avez de contrats publics (interfaces), plus vous pouvez fournir à l'utilisateur la distinction entre l'intention et la mise en œuvre.
la source
En tant que référence pour tous ceux qui traitent également de ce problème et rencontrent cette question, je vais décrire comment nous avons décidé de le résoudre:
Nous suivons essentiellement le chemin indiqué en n ° 2 (classes de wrapper pour les utilitaires statiques). Mais nous ne les utilisons que lorsqu'il est trop complexe pour fournir à l'utilitaire les données nécessaires pour produire le résultat souhaité (c'est-à-dire lorsque nous devons absolument nous moquer de la méthode).
Cela signifie que nous n'avons pas besoin d'écrire un wrapper pour un utilitaire simple comme Apache Commons
StringEscapeUtils
(car les chaînes dont ils ont besoin peuvent facilement être fournies) et que nous n'utilisons pas de simulacre pour des méthodes statiques (si nous pensons que nous aurions peut-être besoin de temps, il est temps d'écrire une classe de wrapper puis moquez une instance du wrapper).la source
Je testerais ces classes en utilisant Groovy . Groovy est simple à ajouter à tout projet Java. Avec cela, vous pouvez facilement simuler les méthodes statiques. Voir Mocking Static Methods utilisant Groovy pour un exemple.
la source
Je travaille pour une grande compagnie d'assurance et notre code source va jusqu'à 400 Mo de fichiers java purs. Nous avons développé l’application entière sans penser au TDD. À partir de janvier de cette année, nous avons commencé les tests Junit pour chaque composant.
La meilleure solution dans notre département consistait à utiliser des objets fantaisie sur certaines méthodes JNI qui étaient fiables (écrites en C) et, de ce fait, vous ne pouviez pas estimer exactement les résultats à chaque fois sur chaque système d'exploitation. Nous n'avions d'autre choix que d'utiliser des classes simulées et des implémentations spécifiques de méthodes JNI dans le seul but de tester chaque module individuel de l'application pour chaque système d'exploitation que nous prenons en charge.
Mais c’était très rapide et cela a très bien fonctionné jusqu’à présent. Je le recommande - http://www.easymock.org/
la source
Les objets interagissent les uns avec les autres pour atteindre un objectif, lorsque vous avez des objets difficiles à tester en raison de l’environnement (point de terminaison de service Web, couche Dao accédant à la base de données, contrôleurs gérant les paramètres de requête http) ou que vous souhaitez tester votre objet isolément, puis vous vous moquez de ces objets.
la nécessité de se moquer des méthodes statiques est une mauvaise odeur, vous devez concevoir votre application plus orientée objet, et les méthodes statiques utilitaires de tests unitaires n’ajoutent pas beaucoup de valeur au projet, la classe wrapper est une bonne approche en fonction de la situation, mais essayez pour tester les objets qui utilisent les méthodes statiques.
la source
Parfois j'utilise l'option 4
Quelque chose comme ça.
Ce que j'aime dans cette approche, c'est qu'elle garde les méthodes utilitaires statiques, ce qui me semble juste lorsque j'essaie d'utiliser la classe dans le code.
la source