À l'origine, TDD provenait du mouvement agile, où les tests étaient écrits à l'avance pour s'assurer que ce que vous codiez restait correct compte tenu des spécifications qui étaient désormais bien définies dans le code de test. Il est également apparu comme un aspect très important de la refactorisation, en ce sens que lorsque vous avez modifié votre code, vous pouvez vous fier aux tests pour prouver que vous n'avez pas changé le comportement du code.
Ensuite, les outils sont venus et ont pensé qu'ils connaissaient des informations sur votre code et pouvaient ensuite générer des talons de test pour vous aider à écrire vos tests unitaires, et je pense que c'est là que tout s'est mal passé.
Les talons de test sont générés par un ordinateur qui n'a aucune idée de ce que vous faites, il produit simplement un talon pour chaque méthode parce que c'est ce qu'on lui dit de faire. Cela signifie que vous disposez d'un cas de test pour chaque méthode, quelle que soit la complexité de cette méthode ou si elle convient pour les tests de manière isolée.
Cela vient des tests du mauvais côté de la méthodologie TDD. Dans TDD, vous êtes censé comprendre ce que le code doit faire, puis produire du code qui y parvient. C'est auto-réalisateur dans la mesure où vous finissez par écrire des tests qui prouvent que le code fait ce que fait le code, pas ce qu'il est censé faire. Combiné à la génération automatique de talons de test basés sur des méthodes, vous perdez à peu près votre temps à prouver chaque petit aspect de votre code qui peut si facilement se tromper lorsque tous les petits morceaux sont assemblés.
Lorsque Fowler a décrit les tests dans son livre, il a fait référence au test de chaque classe avec sa propre méthode principale. Il a amélioré cela, mais le concept est toujours le même - vous testez la classe entière pour qu'elle fonctionne dans son ensemble, tous vos tests sont regroupés pour prouver l'interaction de toutes ces méthodes afin que la classe puisse être réutilisée avec des attentes définies.
Je pense que les boîtes à outils de test nous ont rendu un mauvais service, nous ont conduit à penser que la boîte à outils est le seul moyen de faire les choses alors qu'en réalité, vous devez réfléchir davantage pour vous-même afin d'obtenir le meilleur résultat de votre code. Mettre aveuglément du code de test dans des talons de test pour de petits morceaux signifie simplement que vous devez répéter votre travail dans un test d'intégration de toute façon (et si vous voulez le faire, pourquoi ne pas sauter complètement l'étape de test unitaire désormais redondante). Cela signifie également que les gens perdent beaucoup de temps à essayer d'obtenir une couverture de test à 100% et beaucoup de temps à créer de grandes quantités de code et de données moqueuses qui auraient été mieux utilisées pour rendre le code plus facile à tester d'intégration (c'est-à-dire si vous en avez autant dépendances des données, le test unitaire n'est peut-être pas la meilleure option)
Enfin, la fragilité des tests unitaires basés sur des méthodes ne fait que montrer le problème. Le refactoring est conçu pour être utilisé avec des tests unitaires, si vos tests se cassent tout le temps parce que vous refactorez alors quelque chose a sérieusement mal tourné avec toute l'approche. La refactorisation aime créer et supprimer des méthodes, donc évidemment l'approche de test basée sur une méthode aveugle n'est pas ce qui était initialement prévu.
Je ne doute pas que de nombreuses méthodes obtiendront des tests écrits pour elles, toutes les méthodes publiques d'une classe doivent être testées, mais vous ne pouvez pas vous éloigner du concept de les tester ensemble dans le cadre d'un cas de test unique. Par exemple, si j'ai un ensemble et une méthode get, je peux écrire des tests qui mettent les données et vérifier que les membres internes sont bien définis, ou je peux utiliser chacun pour mettre des données et les extraire à nouveau pour voir si c'est toujours le même et non brouillé. Il s'agit de tester la classe, et non chaque méthode isolément. Si le setter s'appuie sur une méthode privée d'aide, alors c'est très bien - vous n'avez pas besoin de vous moquer de la méthode privée pour vous assurer que le setter fonctionne, pas si vous testez la classe entière.
Je pense que la religion aborde ce sujet, d'où vous voyez le schisme dans ce qui est maintenant connu sous le nom de développement `` axé sur le comportement '' et `` piloté par les tests '' - le concept original des tests unitaires était pour le développement axé sur le comportement.
Comme son nom l'indique, vous testez un sujet atomique dans chaque test. Un tel sujet est généralement une méthode unique. Plusieurs tests peuvent tester la même méthode, afin de couvrir le chemin heureux, les erreurs possibles, etc. Vous testez le comportement, pas la mécanique interne. Le test unitaire consiste donc vraiment à tester l'interface publique d'une classe, c'est-à-dire une méthode spécifique.
Dans les tests unitaires, une méthode doit être testée de manière isolée, c'est-à-dire en stubbing / mocking / fake any dependencies. Sinon, tester une unité avec des dépendances «réelles» en fait un test d'intégration. Il y a un temps et un lieu pour les deux types de tests. Les tests unitaires garantissent qu'un seul sujet fonctionne comme prévu, de manière autonome. Les tests d'intégration garantissent que les «vrais» sujets fonctionnent correctement ensemble.
la source
Ma règle d'or: la plus petite unité de code qui est encore suffisamment complexe pour contenir des bogues.
Qu'il s'agisse d'une méthode ou d'une classe ou d'un sous-système dépend du code particulier, aucune règle générale ne peut être donnée.
Par exemple, il ne fournit aucune valeur pour tester des méthodes getter / setter simples isolément ou des méthodes wrapper qui appellent uniquement une autre méthode. Même une classe entière peut être trop simple à tester, si la classe n'est qu'une enveloppe mince ou un adaptateur. Si la seule chose à tester est si une méthode sur un mock est appelée, alors le code testé est à éclaircir.
Dans d'autres cas, une seule méthode peut effectuer des calculs complexes qui sont précieux à tester de manière isolée.
Dans de nombreux cas, les parties complexes ne sont pas des classes individuelles mais plutôt l'intégration entre les classes. Vous testez donc deux classes ou plus à la fois. Certains diront que ce ne sont pas des tests unitaires mais des tests d'intégration, mais peu importe la terminologie: vous devez tester la complexité et ces tests devraient faire partie de la suite de tests.
la source