Une chose à propos des tests automatisés est que cela nécessite que vous écriviez du code pour pouvoir être testé. Ce n’est pas une mauvaise chose en soi (en fait, c’est bien parce que cela décourage de nombreuses pratiques qui devraient en principe être évitées), mais si vous essayez d’appliquer des tests unitaires au code existant, il y a de fortes chances que ce ne soit pas le cas. été écrit de manière testable.
Des éléments tels que singletons, méthodes statiques, registres, localisateurs de services, etc., introduisent des dépendances très difficiles à simuler. Les violations de la loi de Demeter signifient qu'un trop grand nombre de parties de votre base de code en savent trop sur le fonctionnement d'autres parties de votre base de code, ce qui introduit d'autres dépendances cachées qu'il peut être difficile de casser. Toutes ces choses rendent difficile l'isolement d'un module du reste de la base de code, et si vous ne pouvez pas tester vos modules de manière isolée, les tests unitaires perdent beaucoup de leur valeur. Si un test échoue, cela est-il dû à une défaillance de l'unité testée, ou à une défaillance de l'une de ses dépendances, ou peut-être est-ce dû au fait que les données extraites via une source de données dépendante ne sont pas attendues par le rédacteur de test ? Si tu peux'
La plupart des bases de code que j'ai observées et qui n'ont pas été conçues avec les tests unitaires à l'esprit ont tendance à être fondamentalement impossibles à tester, car les codeurs ont tendance à se concentrer sur l'utilisation du code comme ils le devraient. . Le code qui a été écrit en pensant aux tests unitaires a l’air très différent.
Beaucoup de gens adoptent une approche naïve des tests unitaires lorsqu'ils commencent à le faire pour la première fois. Ils pensent pouvoir simplement écrire une charge de tests pour une base de code existante et tout ira bien, mais cela ne marche jamais comme ça à cause des les questions mentionnées ci-dessus. Ils commencent à découvrir qu'ils doivent exécuter des quantités excessives de configuration dans les tests unitaires pour pouvoir les exécuter. Les résultats sont souvent discutables, car le manque d'isolation dans le code signifie que vous ne pouvez pas localiser la cause d'un échec du test. Ils ont également tendance à commencer par essayer d’écrire des tests "intelligents" qui démontrent un aspect très abstrait du fonctionnement du système. Cela a tendance à échouer car un test unitaire "intelligent" est en soi une source potentielle de bugs. Le test a-t-il échoué à cause d'un bogue dans le module testé, ou à cause d'un bug dans le test? Un test devrait être tellement simple qu'il est évidemment impossible qu'un bogue s'y cache. En fait, les meilleurs tests durent rarement plus de 2 lignes, la première indiquant à l’équipe testée de faire quelque chose, la seconde affirmant que ce qu’elle a fait est conforme aux attentes.
Si votre équipe souhaite sérieusement adopter les tests unitaires, il ne serait pas judicieux de commencer par un projet existant. Les projets existants de votre équipe ne sont probablement pas testables sans refactorisation majeure. Vous feriez mieux d'utiliser un nouveau projet comme base d'apprentissage sur les tests unitaires, car vous avez une table blanche pour travailler. Vous pouvez concevoir la nouvelle base de code de manière à favoriser l’injection de dépendances par rapport aux singletons, aux registres et autres dépendances cachées, vous pouvez l’écrire en fonction des interfaces plutôt que des implémentations, etc. Vous pouvez également (et devriez) écrire les tests avec le code testé, car l'écriture ultérieure des tests aboutit à des tests unitaires garantissant que le module testé fait ce que vous pensez qu'il est censé faire plutôt que ceux qui le testent. ce que les spécifications disent qu'il devrait faire.
Une fois que vous aurez acquis une certaine confiance dans les tests unitaires, votre équipe commencera probablement à comprendre les failles de son code existant qui vont faire obstacle aux tests unitaires. C’est à ce moment-là que vous pouvez commencer à travailler à la refactorisation du code existant pour le rendre plus testable. Ne soyez pas ambitieux et essayez de faire tout cela en même temps, ou tentez de remplacer un système qui fonctionne par un tout nouveau, commencez simplement par trouver les éléments de la base de code qui peuvent facilement être testés (ceux qui ne le sont pas. dépendances ou où les dépendances sont évidentes) et écrivez des tests pour celles-ci. Je sais que j'ai dit que l'écriture d'un test avec le code était préférable à l'écriture de tests après, mais même un test écrit plus tard a toujours une valeur comme point de départ. Ecrivez les tests comme si vous ne saviez rien du fonctionnement de la classe, si ce n’était ce que ses spécifications prévoient de faire. Lorsque vous exécutez les tests et obtenez des échecs, les spécifications ou l'implémentation sont incorrectes. Vérifiez deux fois pour déterminer ce qui est faux et mettez à jour le test ou le code en conséquence.
Une fois que vous avez récolté les fruits faciles, votre vrai travail commence. Vous devez commencer à rechercher les dépendances cachées dans votre base de code et à les corriger, une à la fois. Ne soyez pas trop ambitieux à ce stade, tenez-vous en à ne faire qu'un module à la fois, ou même un seul problème dans un module, jusqu'à ce que les obstacles au test soient corrigés et que vous puissiez passer au suivant.
TL: DR: La plupart des gens pensent que les tests sont faciles et que vous pouvez facilement intégrer des tests dans du code existant. Ces deux hypothèses sont fausses. Si vous vous lancez dans un projet visant à intégrer des tests unitaires à vos projets en gardant à l'esprit ces deux faits, vous aurez plus de chances de réussir.