Nous avons essayé d’introduire des tests unitaires dans notre projet actuel, mais cela ne semble pas fonctionner. Le code supplémentaire semble être devenu un casse-tête de maintenance, car lorsque notre framework interne change, nous devons corriger tous les tests unitaires qui y sont liés.
Nous avons une classe de base abstraite pour les tests unitaires de nos contrôleurs, qui agit comme un modèle appelant les implémentations de méthodes abstraites des classes enfants, c.-à-d.
J’étais un défenseur des tests unitaires, mais cela ne semble pas fonctionner sur notre projet actuel.
Quelqu’un peut-il aider à identifier le problème et comment nous pouvons faire fonctionner les tests unitaires pour nous plutôt que contre nous?
Les tests peuvent être difficiles à maintenir s’ils sont écrits sur un code de type procédural qui repose fortement sur l’état global ou qui se trouve profondément dans le corps d’une méthode laide. Si vous écrivez du code dans un langage OO, utilisez les constructions OO efficacement pour réduire cela.
Il y a des points d’étranglement dans le code qui changent beaucoup plus fréquemment que les autres pièces. Faites cela dans votre base de code et vos tests deviendront plus sains.
Faites tout ce que vous pouvez pour réduire les tests et le contexte dans lequel ils sont exécutés.
Vous ne pourrez probablement pas supprimer toutes les duplications, mais essayez tout de même de les supprimer là où elles causent de la douleur. Assurez-vous de ne pas supprimer autant de doublons que quelqu’un ne peut pas entrer et dire ce que le test fait en un coup d’œil. (Voir l’article de Paul Wheaton intitulé “Evil Unit Tests” pour une explication alternative du même concept.)
Pensez à la complexité d’un test Selenium en enregistrement et en lecture et à ce qui pourrait changer sous vos yeux plutôt que de tester une méthode unique.
Les vrais tests unitaires nécessitent une véritable isolation. Les tests unitaires ne touchent pas une firebase database ou des sockets ouverts. Arrêtez de vous moquer de ces interactions. Vérifiez que vous parlez correctement à vos collaborateurs, et non que le résultat correct de cet appel de méthode était “42”.
C’est à débattre pour savoir si une équipe donnée va ou non tester tous les codes, ou écrire “tests en premier” pour chaque ligne de code. Mais devraient-ils d’abord écrire au moins quelques tests? Absolument. Il existe des scénarios dans lesquels le test-first est sans aucun doute le meilleur moyen d’aborder un problème.
Testez-vous des unités de code suffisamment petites? Vous ne devriez pas voir trop de changements à moins que vous ne changiez fondamentalement tout dans votre code principal.
Une fois que les choses sont stables, vous apprécierez le fait que l’unité teste davantage, mais même maintenant, vos tests mettent en évidence la mesure dans laquelle les modifications apscopes à votre framework sont transmises.
Cela en vaut la peine, respectez-le du mieux que vous pouvez.
Sans plus d’informations, il est difficile de comprendre pourquoi vous souffrez de ces problèmes. Parfois, il est inévitable que le changement d’interface, etc., brise beaucoup de choses, d’autres fois, c’est dû à des problèmes de conception.
C’est une bonne idée d’essayer de classer les échecs par catégories. Quel genre de problèmes rencontrez-vous? Par exemple, est-ce qu’il teste la maintenance (comme en les faisant comstackr après le refactoring!) En raison des modifications de l’API, ou est-ce que le comportement de l’API change? Si vous pouvez voir un modèle, vous pouvez alors essayer de modifier la conception du code de production ou mieux isoler les tests.
Si le fait de modifier une poignée de choses cause des dégâts considérables à votre suite de tests dans de nombreux endroits, vous pouvez faire certaines choses (la plupart ne sont que des conseils de tests unitaires courants):
Développer de petites unités de code et tester de petites unités de code. Extrayez les interfaces ou les classes de base là où elles ont du sens, de sorte que les unités de code contiennent des «jointures». Plus vous aurez de dépendances (ou pire, instanciez dans la classe avec ‘new’), plus vous serez exposé à changer votre code. Si chaque unité de code a une poignée de dépendances (parfois un couple ou pas du tout), alors il est préférable de l’isoler du changement.
N’affirmez jamais ce dont le test a besoin. N’affirmez pas sur un état intermédiaire, accidentel ou indépendant. Conception par contrat et test par contrat (par exemple, si vous testez une méthode de stack pop, ne testez pas la propriété count après le push – cela devrait faire l’object d’un test séparé).
Je vois ce problème assez, surtout si chaque test est une variante. Si l’un de ces états incidents change, il rompt tout ce qui y est associé (que les assertions soient nécessaires ou non).
Comme avec le code normal, utilisez les usines et les constructeurs dans vos tests unitaires. J’ai appris que lorsque 40 tests environ nécessitaient un appel de constructeur mis à jour après un changement d’API …
Tout aussi important, utilisez la porte d’entrée en premier. Vos tests doivent toujours utiliser l’état normal s’il est disponible. Seulement utilisé test basé sur l’interaction lorsque vous devez (c’est-à-dire pas d’état à vérifier).
Quoi qu’il en soit, j’essayerais de savoir pourquoi et où les tests vont et viennent. Faites de votre mieux pour vous protéger du changement.
L’un des avantages des tests unitaires est que lorsque vous apportez de telles modifications, vous pouvez prouver que vous ne cassez pas votre code. Vous devez garder vos tests en phase avec votre framework, mais ce travail plutôt banal est beaucoup plus facile que d’essayer de comprendre ce qui a été cassé lors de la refactorisation.
Je vous insiste pour restr avec le TDD. Essayez de vérifier votre cadre de test d’unité, faites une parsing de cause racine avec votre équipe et identifiez la zone.
Corrigez le code de test de l’unité au niveau de la suite et ne changez pas fréquemment votre code spécialement les noms de fonction ou d’autres modules.
Je vous serais reconnaissant si vous pouviez bien partager votre étude de cas, alors nous pourrions en savoir plus sur le problème?
Bonne question!
Concevoir de bons tests unitaires est difficile à concevoir en tant que tel. Ceci est rarement reconnu par les développeurs, et le résultat est souvent des tests unitaires écrits à la hâte qui nécessitent une maintenance chaque fois que le système testé change. Ainsi, une partie de la solution à votre problème pourrait consister à passer plus de temps à améliorer la conception de vos tests unitaires.
Je peux recommander un bon livre qui mérite sa facturation en tant que modèles de conception des tests unitaires
HTH
Si le problème est que vos tests ne sont plus à jour avec le code actuel, vous pouvez effectuer l’une des opérations suivantes:
Eh bien, si la logique a changé dans le code et que vous avez des tests écrits pour ces parties de code, je suppose que les tests devront être modifiés pour vérifier la nouvelle logique. Les tests unitaires sont supposés être un code assez simple qui teste la logique de votre code.
Vos tests unitaires font ce qu’ils sont censés faire. Apportez à la surface des ruptures de comportement dues à des modifications du cadre, du code immédiat ou d’autres sources externes. Ce que vous devez faire est de vous aider à déterminer si le comportement a changé et si les tests unitaires doivent être modifiés en conséquence, ou si un bogue a été introduit, entraînant l’échec du test unitaire et nécessitant une correction.
N’abandonnez pas, même si c’est frustrant maintenant, le bénéfice sera réalisé.
Je ne suis pas sûr des problèmes spécifiques qui rendent difficile la maintenance des tests pour votre code, mais je peux partager certaines de mes propres expériences lorsque j’ai rencontré des problèmes similaires lors de la rupture de mes tests. J’ai finalement appris que le manque de testabilité était dû en grande partie à certains problèmes de conception de la classe testée:
À cause de cela, j’ai constaté que mes tests étaient généralement en train de se briser – pas à cause d’un changement de classe testé – mais en raison de changements dans les autres classes appelées par la classe testée. En général, les classes de refactoring pour demander leurs dépendances de données et les tests avec des objects fictifs (EasyMock et al pour Java) rendent les tests beaucoup plus ciblés et maintenables. J’ai vraiment apprécié certains sites en particulier sur ce sujet:
Pourquoi devriez-vous modifier vos tests unitaires chaque fois que vous apportez des modifications à votre framework? Cela ne devrait-il pas être l’inverse?
Si vous utilisez TDD, vous devez d’abord décider que vos tests testent le comportement incorrect et qu’ils doivent plutôt vérifier que le comportement souhaité existe. Maintenant que vous avez corrigé vos tests, vos tests échouent et vous devez éliminer les bogues dans votre framework jusqu’à ce que vos tests soient de nouveau réussis.
Tout vient avec le prix du cours bien sûr. À ce stade précoce du développement, il est normal que de nombreux tests unitaires soient modifiés.
Vous voudrez peut-être examiner quelques bits de votre code pour faire plus d’encapsulation, créer moins de dépendances, etc.
Lorsque vous approchez de la date de production, vous serez heureux d’avoir ces tests, croyez-moi 🙂
Vos tests unitaires ne sont-ils pas trop orientés vers la boîte noire? Je veux dire … laissez-moi prendre un exemple: supposons que vous testez une sorte de conteneur, utilisez-vous la méthode get () du conteneur pour vérifier qu’un nouvel élément a bien été stocké, ou obtenez-vous un handle vers le stockage réel pour récupérer l’article directement où il est stocké? Le dernier fait des tests fragiles: lorsque vous modifiez l’implémentation, vous ne respectez pas les tests.
Vous devez tester les interfaces, pas l’implémentation interne.
Et quand vous changez de framework, vous feriez mieux d’essayer de changer les tests d’abord, puis le framework.
Je suggère d’investir dans un outil d’automatisation des tests. Si vous utilisez une continuous integration, vous pouvez la faire fonctionner en tandem. Il y a des outils là-bas qui vont scanner votre code et vont générer des tests pour vous. Alors les exécutera. L’inconvénient de cette approche est que c’est trop générique. Parce que dans de nombreux cas, le test unitaire a pour but de briser le système. J’ai écrit de nombreux tests et oui, je dois les changer si le code change.
Il y a une ligne fine avec l’outil d’automatisation dont vous auriez définitivement une meilleure couverture de code.
Cependant, avec des tests bien conçus, vous testerez également l’intégrité du système.
J’espère que cela t’aides.
Si votre code est vraiment difficile à tester et que le code de test se casse ou nécessite beaucoup d’efforts pour restr synchronisé, alors vous avez un problème plus important.
Envisagez d’utiliser le refactoring de la méthode extract pour extraire de petits blocs de code qui ne font qu’une chose et une seule chose. sans dépendances et écrivez vos tests sur ces petites méthodes.
Le code supplémentaire semble être devenu un casse-tête de maintenance, car lorsque notre framework interne change, nous devons corriger tous les tests unitaires qui y sont liés.
L’alternative est que lorsque votre Framework change, vous ne testez pas les modifications. Ou vous ne testez pas le Framework du tout. Est-ce que c’est ce que tu veux?
Vous pouvez essayer de refactoriser votre Framework afin qu’il soit composé d’éléments plus petits pouvant être testés indépendamment. Ensuite, lorsque votre structure change, vous espérez que (a) moins de pièces changent ou (b) les modifications concernent principalement la manière dont les pièces sont composées. De toute façon, vous obtiendrez une meilleure réutilisation du code et des tests. Mais un véritable effort intellectuel est impliqué; ne vous attendez pas à ce que ce soit facile.
Les tests unitaires finissent par tester l’interaction de plusieurs classes, ce qui les rend très complexes, et donc fragiles.
Ce que je veux dire, c’est que bon nombre des techniques de développement de logiciels novasortingces ne fonctionnent qu’en cas d’utilisation conjointe. En particulier MVC, ORM, IoC, tests unitaires et Mocking. Le DDD (au sens primitif moderne) et le TDD / BDD sont plus indépendants, vous pouvez donc les utiliser ou non.
Parfois, la conception des tests TDD lance des interrogations sur la conception de l’application elle-même. Vérifiez si vos classes ont été bien conçues, vos méthodes ne font qu’une chose à la fois … Avec une bonne conception, il devrait être simple d’écrire du code pour tester des méthodes et des classes simples.
J’ai moi-même réfléchi à ce sujet. Je suis très vendu sur la valeur des tests unitaires, mais pas sur le TDD ssortingct. Il me semble que, jusqu’à un certain point, vous pourriez faire de la programmation exploratoire où la façon dont les choses sont divisées en classes / interfaces devra être modifiée. Si vous avez investi beaucoup de temps dans les tests unitaires pour l’ancienne structure de classe, cela augmente l’inertie par rapport au refactoring, ce qui est pénible de rejeter ce code supplémentaire, etc.