Je suis religieusement TDD. Mes projets ont généralement une couverture de test de 85% ou mieux, avec des cas de test significatifs.
Je fais beaucoup de travail avec HBase , et l'interface client principale, HTable, est une vraie douleur à se moquer. Il me faut 3 ou 4 fois plus de temps pour écrire mes tests unitaires que pour écrire des tests qui utilisent un point de terminaison en direct.
Je sais que, philosophiquement, les tests qui utilisent des simulations devraient avoir la priorité sur les tests qui utilisent un point de terminaison en direct. Mais se moquer de HTable est une douleur sérieuse, et je ne suis pas vraiment sûr qu'il offre beaucoup d'avantages par rapport aux tests contre une instance HBase en direct.
Tous les membres de mon équipe exécutent une instance HBase à nœud unique sur leur poste de travail, et nous avons des instances HBase à nœud unique exécutées sur nos boîtes Jenkins, ce n'est donc pas un problème de disponibilité. Les tests de point de terminaison en direct prennent évidemment plus de temps à exécuter que les tests qui utilisent des simulations, mais cela ne nous intéresse pas vraiment.
En ce moment, j'écris des tests de point de terminaison en direct ET des tests sur maquette pour toutes mes classes. J'adorerais abandonner les moqueries, mais je ne veux pas que la qualité diminue en conséquence.
Qu'en pensez-vous tous?
la source
Réponses:
Ma première recommandation serait de ne pas se moquer des types que vous ne possédez pas . Vous avez mentionné que HTable était une vraie douleur à se moquer - peut-être devriez-vous l'envelopper à la place dans un adaptateur qui expose les 20% des fonctionnalités de HTable dont vous avez besoin, et se moquer de l'emballage si nécessaire.
Cela étant dit, supposons que nous parlons de types que vous possédez tous. Si vos tests factices se concentrent sur des scénarios de cheminement heureux où tout se passe bien, vous ne perdrez rien en les abandonnant car vos tests d'intégration testent probablement déjà les mêmes chemins exacts.
Cependant, les tests isolés deviennent intéressants lorsque vous commencez à réfléchir à la façon dont votre système sous test devrait réagir à chaque petite chose qui pourrait se produire comme défini dans le contrat de son collaborateur, quel que soit l'objet concret réel avec lequel il parle. Cela fait partie de ce que certains appellent l'exactitude de base . Il pourrait y avoir beaucoup de ces petits cas et beaucoup plus de combinaisons d'entre eux. C'est là que les tests d'intégration commencent à devenir moche alors que les tests isolés resteront rapides et gérables.
Pour être plus concret, que se passe-t-il si l'une des méthodes de votre adaptateur HTable renvoie une liste vide? Et si elle retourne null? Que faire s'il déclenche une exception de connexion? Il devrait être défini dans le contrat de l'adaptateur si l'une de ces choses pouvait se produire, et n'importe lequel de ses consommateurs devrait être prêt à faire face à ces situations , d'où la nécessité de tests pour eux.
Pour résumer: vous ne verrez aucune baisse de qualité en supprimant vos tests factices s'ils ont testé exactement les mêmes choses que vos tests d'intégration . Cependant, essayer d'imaginer des tests isolés supplémentaires (et des tests de contrat ) peut vous aider à réfléchir largement à vos interfaces / contrats et à améliorer la qualité en s'attaquant aux défauts qui auraient été difficiles à penser et / ou lents à tester avec des tests d'intégration.
la source
Je pense à tout le moins, qui est un point de courant controverse entre les partisans TDD.
Mon point de vue personnel va au-delà de cela pour dire qu'un test basé sur une simulation est principalement un moyen de représenter une forme de contrat d'interface ; idéalement, il casse (c.-à-d. échoue) si et seulement si vous changez l'interface . Et en tant que tel, dans des langages raisonnablement fortement typés comme Java, et lorsque vous utilisez une interface définie explicitement, c'est presque entièrement superflu: le compilateur vous aura déjà dit si vous avez changé l'interface.
La principale exception est lorsque vous utilisez une interface très générique, peut-être basée sur des annotations ou des réflexions, que le compilateur n'est pas en mesure de contrôler automatiquement de manière utile. Même dans ce cas, vous devriez vérifier s'il existe un moyen de faire la validation par programme (par exemple, une bibliothèque de vérification de syntaxe SQL) plutôt qu'à la main à l'aide de simulacres.
C'est ce dernier cas que vous faites lorsque vous testez avec une base de données locale «en direct»; l'implémentation de htable entre en jeu et applique une validation beaucoup plus complète du contrat interfacve que vous ne le pensez jamais à écrire à la main.
Malheureusement, une utilisation beaucoup plus courante des tests factices est le test qui:
Ces tests doivent bien sûr être supprimés à vue.
la source
Combien de temps un test basé sur un point de terminaison prend-il pour s'exécuter qu'un test basé sur une simulation? Si c'est beaucoup plus long, alors oui, cela vaut la peine d'investir votre temps d'écriture de test pour rendre les tests unitaires plus rapides - car vous devrez les exécuter plusieurs fois. Si ce n'est pas beaucoup plus long, même si les tests basés sur les points de terminaison ne sont pas des tests unitaires «purs», tant qu'ils font un bon travail de test de l'unité, il n'y a aucune raison d'être religieux à ce sujet.
la source
Je suis entièrement d'accord avec la réponse de guillaume31, ne vous moquez jamais de types que vous ne possédez pas!.
Normalement, une douleur dans le test (se moquer d'une interface complexe) reflète un problème dans votre conception. Peut-être avez-vous besoin d'une abstraction entre votre modèle et votre code d'accès aux données, un exemple de formulaire utilisant une architecture hexagonale et un modèle de référentiel est le moyen le plus courant de résoudre ce type de problèmes.
Si vous voulez faire un test d'intégration pour vérifier les choses faites un test d'intégration, si vous voulez faire un test unitaire parce que vous testez votre logique, faites un test unitaire et isolez la persistance. Mais faire un test d'intégration parce que vous ne savez pas comment isoler votre logique d'un système externe (ou parce que l'isoler est une douleur) c'est une grosse odeur, vous choisissez l'intégration plutôt que l'unité pour une limitation dans votre conception et non pour un besoin réel pour tester l'intégration.
Jetez un oeil à ce formulaire de discussion Ian Cooper: http://vimeo.com/68375232 , il parle d'architecture hexagonale et de tests, il parle de quand et de quoi se moquer, un discours vraiment inspiré qui résout de nombreuses questions comme la vôtre sur le vrai TDD .
la source
TL; DR - Selon moi, cela dépend de l'effort que vous finissez par dépenser pour les tests, et s'il aurait été préférable de le dépenser davantage sur votre système actuel.
Version longue:
Quelques bonnes réponses ici, mais mon point de vue est différent: les tests sont une activité économique qui doit être rentabilisée, et si le temps que vous passez n'est pas retourné dans le développement et la fiabilité du système (ou tout ce que vous cherchez à sortir) de tests) alors vous faites peut-être un mauvais investissement; vous êtes en train de construire des systèmes, pas d'écrire des tests. Par conséquent, la réduction de l'effort d'écriture et de maintenance des tests est cruciale.
Par exemple, certaines valeurs principales que je gagne des tests sont:
Les tests par rapport à un point de terminaison en direct devraient toujours les fournir.
Quelques inconvénients pour les tests sur un point de terminaison en direct:
Si j'étais dans cette situation et que les inconvénients ne semblaient pas être un problème alors que se moquer du point de terminaison ralentissait considérablement l'écriture de mon test, je testerais un point de terminaison en direct en un clin d'œil, tant que je serais sûr de vérifiez à nouveau après un certain temps pour voir que les inconvénients ne s'avèrent pas être un problème dans la pratique.
la source
Du point de vue des tests, certaines exigences sont absolument indispensables:
C'est un grand défi lorsque vous vous connectez à une source qui maintient un état en dehors de vos tests. Ce n'est pas du "pur" TDD, mais l'équipe de Ruby on Rails a résolu ce problème d'une manière qui pourrait être adaptée à vos besoins. Le framework de test des rails a fonctionné de cette manière:
Tout ce travail a été intégré au harnais de test et il fonctionne assez bien. Il y a beaucoup plus, mais les bases sont suffisantes pour cette conversation.
Dans les différentes équipes avec lesquelles j'ai travaillé au fil du temps, nous ferions des choix qui favoriseraient le test du code même si ce n'était pas le chemin le plus correct. Idéalement, nous emballerions tous les appels vers un magasin de données avec du code que nous contrôlions. En théorie, si l'un de ces anciens projets obtenait un nouveau financement, nous pourrions revenir en arrière et le déplacer de la base de données à Hadoop en concentrant notre attention sur une poignée de classes seulement.
Les aspects importants ne sont pas de jouer avec les données de production et de vous assurer que vous testez vraiment ce que vous pensez tester. Il est vraiment important de pouvoir réinitialiser le service externe sur une base de référence connue à la demande, même à partir de votre code.
la source