Cet article est une adaptation d'un chapitre du livre The Complete Software Career Guide. Son auteur, John Sonmez, l'écrit et publie quelques chapitres sur son site Internet.
What такое TDD и модульное тестирование [перевод] - 1

Un petit glossaire pour les débutants

Les tests unitaires ou tests unitaires sont un processus de programmation qui vous permet de vérifier l’exactitude des modules individuels du code source d’un programme. L'idée est d'écrire des tests pour chaque fonction ou méthode non triviale. Les tests de régression sont un nom général désignant tous les types de tests logiciels visant à détecter des erreurs dans des zones déjà testées du code source. De telles erreurs - lorsque, après avoir apporté des modifications au programme, quelque chose qui devrait continuer à fonctionner cesse de fonctionner - sont appelées erreurs de régression. Résultat rouge, échec - échec du test. La différence entre le résultat attendu et le résultat réel. Résultat vert, réussite - résultat du test positif. Le résultat réel n'est pas différent de ce qui a été obtenu. ***
What такое TDD и модульное тестирование [перевод] - 2
J'ai une relation très mitigée avec le Test Driven Development (TDD) et les tests unitaires, allant de l'amour à la haine et vice-versa. J’étais un fervent fan et en même temps un sceptique suspicieux quant à l’utilisation de cette « meilleure pratique » et d’autres. La raison de mon attitude est basée sur le fait qu'un problème sérieux est apparu dans les processus de développement logiciel : les développeurs, et parfois les managers, utilisent certains outils et méthodologies uniquement parce qu'ils appartiennent aux « meilleures pratiques ». La véritable raison de leur utilisation reste floue. Un jour, j'ai commencé à travailler sur un certain projet et, ce faisant, j'ai été informé que nous allions modifier du code couvert par un grand nombre de tests unitaires. Sans blague, il y en avait environ 3 000. C'est généralement un bon signe, un signal que les développeurs utilisent des méthodologies avancées. Le code avec cette approche est le plus souvent structuré et repose sur une architecture bien pensée. En un mot, la présence de tests me faisait plaisir, ne serait-ce que parce que cela facilitait mon travail de mentor auprès des programmeurs. Comme nous avions déjà des tests unitaires, tout ce que j'avais à faire était de connecter l'équipe de développement pour les soutenir et commencer à écrire notre propre code. J'ai ouvert l'IDE (environnement de développement intégré) et chargé le projet.
What такое TDD и модульное тестирование [перевод] - 3
C'était un gros projet ! J'ai trouvé un dossier intitulé "tests unitaires". "Super", ai-je pensé. - Lançons-le et voyons ce qui se passe. Cela n'a pris que quelques minutes, et à ma grande surprise, tous les tests ont réussi, tout était vert ( « vert » est un résultat positif du test. Signifie que le code fonctionne comme prévu. Le rouge indique un « échec » ou un échec, puis il arrive parfois que le code ne fonctionne pas correctement - note du traducteur ). Ils ont tous réussi le test. À ce moment-là, le sceptique en moi s’est réveillé. Comment se fait-il que trois mille tests unitaires les aient tous effectués en même temps et aient donné un résultat positif ? Au cours de ma longue pratique, je ne me souvenais pas d'un moment où j'avais commencé à travailler sur un projet sans un seul test unitaire négatif dans le code. Ce qu'il faut faire? Vérifiez manuellement ! ChY a choisi un test aléatoire, pas le plus révélateur, mais ce qu'il vérifiait était immédiatement clair. Mais en y travaillant, j'ai remarqué quelque chose d'absurde : le test ne contenait aucune comparaison avec le résultat attendu (affirmations) ! Autrement dit, en réalité, rien n’a été vérifié ! Il y avait certaines étapes dans le test, elles ont été réalisées, mais à la fin du test, où il devait comparer les résultats réels et attendus, il n'y a eu aucun contrôle. Le "test" n'a rien testé. J'ai ouvert un autre test. Mieux encore : l'opérateur de comparaison avec le résultat a été commenté. Brillamment! C'est un excellent moyen de réussir un test, il suffit de commenter le code qui provoque son échec. J'ai vérifié un autre test, puis un autre... Aucun d'eux n'a rien vérifié. Trois mille tests, et tous sont totalement inutiles. Il existe une énorme différence entre écrire des tests unitaires et comprendre les tests unitaires et le développement piloté par les tests (TDD).

Qu’est-ce que les tests unitaires ?

What такое TDD и модульное тестирование [перевод] - 4
L'idée de base des tests unitaires est d'écrire des tests qui testent la plus petite « unité » de code. Les tests unitaires sont généralement écrits dans le même langage de programmation que le code source de l'application. Ils sont créés directement pour tester ce code. Autrement dit, les tests unitaires sont du code qui vérifie l'exactitude d'un autre code. J'utilise le mot « test » de manière assez libérale dans le contexte, car les tests unitaires, dans un certain sens, ne sont pas des tests. Ils ne vivent rien. Ce que je veux dire, c'est que lorsque vous exécutez un test unitaire, vous ne constatez généralement pas qu'un code ne fonctionne pas. Vous le découvrez en écrivant le test, car vous modifierez le code jusqu'à ce que le test devienne vert. Oui, le code peut changer ultérieurement et votre test peut alors échouer. En ce sens, un test unitaire est donc un test de régression. Un test unitaire n'est pas comme un test normal où vous devez suivre quelques étapes et voir si le logiciel fonctionne correctement ou non. Au cours du processus d'écriture d'un test unitaire, vous découvrez si le code fait ce qu'il est censé faire ou non, et vous modifierez le code jusqu'à ce que le test réussisse.
What такое TDD и модульное тестирование [перевод] - 5
Pourquoi ne pas écrire un test unitaire et vérifier s'il réussit ? Si vous y réfléchissez de cette façon, les tests unitaires se transforment en une sorte d'exigences absolues pour certains modules de code à un niveau très bas. Vous pouvez considérer un test unitaire comme une spécification absolue . Un test unitaire détermine que dans ces conditions, avec cet ensemble particulier d'entrées, il existe une sortie que vous devriez obtenir de cette unité de code. Les véritables tests unitaires identifient la plus petite unité de code cohérente qui, dans la plupart des langages de programmation - du moins ceux orientés objet - est une classe.

Qu'est-ce qu'on appelle parfois les tests unitaires ?

What такое TDD и модульное тестирование [перевод] - 6
Les tests unitaires sont souvent confondus avec les tests d'intégration. Certains « tests unitaires » testent plusieurs classes ou testent de grandes unités de code. De nombreux développeurs prétendent qu'ils écrivent des tests unitaires alors qu'en fait ils écrivent des tests whitebox de bas niveau. Ne discutez pas avec ces gars. Sachez simplement qu'ils écrivent en fait des tests d'intégration et que les véritables tests unitaires testent la plus petite unité de code indépendamment des autres parties. Une autre chose souvent appelée tests unitaires concerne les tests unitaires sans vérification par rapport à une valeur attendue. En d’autres termes, des tests unitaires qui ne testent rien. Tout test, unitisé ou non, doit inclure une sorte de vérification - nous appelons cela la vérification du résultat réel par rapport au résultat attendu. C'est cette réconciliation qui détermine si le test réussit ou échoue. Un test qui réussit toujours est inutile. Un test qui échoue toujours est inutile.

La valeur des tests unitaires

Pourquoi suis-je un passionné des tests unitaires ? Pourquoi est-il dangereux d’appeler les tests généralisés, qui impliquent de tester non pas le plus petit bloc isolé d’un autre code, mais un plus grand morceau de code, « tests unitaires » ? Quel est le problème si certains de mes tests ne comparent pas les résultats reçus et attendus ? Au moins, ils exécutent le code. Je vais essayer de t'expliquer.
What такое TDD и модульное тестирование [перевод] - 7
Il existe deux raisons principales d’effectuer des tests unitaires. La première consiste à améliorer la conception du code. Rappelez-vous comment j'ai dit que les tests unitaires ne sont pas vraiment des tests ? Lorsque vous écrivez des tests unitaires appropriés, vous vous forcez à isoler la plus petite unité de code. Ces tentatives vous amèneront à découvrir des problèmes dans la structure du code lui-même. Vous aurez peut-être beaucoup de mal à isoler la classe de test et à ne pas inclure ses dépendances, ce qui peut vous faire réaliser que votre code est trop étroitement couplé. Vous constaterez peut-être que la fonctionnalité de base que vous essayez de tester s'étend sur plusieurs modules, ce qui vous amène à croire que votre code n'est pas suffisamment cohérent. Lorsque vous vous asseyez pour écrire un test unitaire, vous découvrirez peut-être soudainement (et croyez-moi, cela arrive !) que vous n'avez aucune idée de ce que le code est censé faire. Par conséquent, vous ne pourrez pas écrire de test unitaire pour celui-ci. Et bien sûr, vous pouvez trouver un vrai bug dans l’implémentation du code, puisque le test unitaire vous oblige à sortir des sentiers battus et à tester différents ensembles d’entrées que vous n’avez peut-être pas pris en compte.
What такое TDD и модульное тестирование [перевод] - 8
Si vous respectez strictement la règle « tester la plus petite unité de code indépendamment des autres » lors de la création de tests unitaires, vous rencontrerez forcément toutes sortes de problèmes avec ce code et la conception de ces modules. Dans le cycle de vie du développement logiciel, les tests unitaires sont davantage une activité d'évaluation qu'une activité de test. Le deuxième objectif principal des tests unitaires est de créer un ensemble automatisé de tests de régression pouvant servir de spécification de bas niveau du comportement du logiciel. Qu'est-ce que ça veut dire? Lorsque vous pétrissez la pâte, vous ne la cassez pas. De ce point de vue, les tests unitaires sont des tests, plus précisément des tests de régression. Cependant, le but des tests unitaires n’est pas simplement de créer des tests de régression. En pratique, les tests unitaires détectent rarement les régressions, car une modification de l'unité de code que vous testez contient presque toujours des modifications du test unitaire lui-même. Les tests de régression sont beaucoup plus efficaces à un niveau supérieur, lorsque le code est testé comme une « boîte noire », car à ce niveau la structure interne du code peut être modifiée, tandis que le comportement externe est censé rester le même. Les tests unitaires vérifient à leur tour la structure interne afin que lorsque cette structure change, les tests unitaires n'échouent pas. Ils deviennent inutilisables et doivent désormais être modifiés, jetés ou réécrits. Vous en savez désormais plus sur le véritable objectif des tests unitaires que de nombreux développeurs de logiciels chevronnés.

Qu'est-ce que le développement piloté par les tests (TDD) ?

What такое TDD и модульное тестирование [перевод] - 9
Dans le processus de développement logiciel, une bonne spécification vaut son pesant d’or. L'approche TDD est qu'avant d'écrire un code, vous écrivez d'abord un test qui servira de spécification, c'est-à-dire définira ce que le code doit faire. Il s’agit d’un concept de développement logiciel extrêmement puissant, mais il est souvent mal utilisé. En règle générale, le développement piloté par les tests consiste à utiliser des tests unitaires pour guider la création du code d'application. Mais en réalité, cette approche peut être appliquée à n’importe quel niveau. Cependant, dans cet article, nous supposerons que nous utilisons des tests unitaires pour notre application. L'approche TDD bouleverse tout, et au lieu d'écrire d'abord du code, puis d'écrire des tests unitaires pour tester ce code, vous écrivez d'abord un test unitaire, puis écrivez du code pour rendre ce test vert. De cette manière, les tests unitaires « pilotent » le développement du code. Ce processus est répété encore et encore. Vous écrivez un autre test qui définit davantage de fonctionnalités sur ce que le code doit faire. Vous écrivez et modifiez ensuite le code pour vous assurer que le test se termine avec succès. Une fois que vous avez un résultat vert, vous commencez à refactoriser le code, c'est-à-dire à le refactoriser ou à le nettoyer pour le rendre plus concis. Cette chaîne de processus est souvent appelée « Rouge-Vert-Refactoring » car d'abord le test unitaire échoue (rouge), puis le code est écrit pour s'adapter au test, en s'assurant qu'il réussit (vert), et enfin le code est optimisé ( refactorisation). .

Quel est le but du TDD ?

What такое TDD и модульное тестирование [перевод] - 10
Le développement piloté par les tests (TDD), comme les tests unitaires, peut être utilisé de manière incorrecte. Il est très facile d'appeler ce que vous faites "TDD" et même de suivre la pratique sans comprendre pourquoi vous le faites de cette façon. La plus grande valeur du TDD réside dans le fait que des tests sont effectués pour produire des spécifications de qualité. TDD consiste essentiellement à rédiger des spécifications précises qui peuvent être automatiquement vérifiées avant l’écriture du codage. Les tests sont les meilleures spécifications car ils ne mentent pas. Ils ne vous le diront pas après deux semaines de tourments avec le code « ce n’est pas du tout ce que je voulais dire ». Les tests, lorsqu’ils sont rédigés correctement, réussissent ou échouent. Les tests indiquent clairement ce qui devrait se produire dans certaines circonstances. Ainsi, l'objectif de TDD est de nous donner une compréhension complète de ce que nous devons mettre en œuvre avant de commencer à le mettre en œuvre. Si vous débutez avec TDD et que vous ne parvenez pas à comprendre ce que le test est censé tester, vous devez alors poser plus de questions. Un autre rôle important de TDD est de préserver et d'optimiser le code. La maintenance du code coûte cher. Je plaisante souvent en disant que le meilleur programmeur est celui qui écrit le code le plus court qui résout un problème. Ou même celui qui prouve que ce problème n'a pas besoin d'être résolu, et supprime ainsi complètement le code, puisque c'est ce programmeur qui a trouvé le bon moyen de réduire le nombre d'erreurs et de réduire le coût de maintenance de l'application. En utilisant TDD, vous pouvez être absolument sûr que vous n’écrivez pas de code inutile, puisque vous n’écrirez que du code pour réussir les tests. Il existe un principe de développement logiciel appelé YAGNI (vous n'en aurez pas besoin). TDD empêche YAGNI.

Flux de travail typique de développement piloté par les tests (TDD)

What такое TDD и модульное тестирование [перевод] - 11
Comprendre la signification de TDD d’un point de vue purement académique est difficile. Regardons donc un exemple de session TDD. Imaginez-vous assis à votre bureau et esquissez rapidement ce que vous pensez être une conception de haut niveau pour une fonctionnalité permettant à un utilisateur de se connecter à une application et de modifier son mot de passe s'il l'oublie. Vous décidez de commencer par la première implémentation de la fonction de connexion, en créant une classe qui gérera toute la logique du processus de connexion. Vous ouvrez votre éditeur préféré et créez un test unitaire appelé "Une connexion vide empêche l'utilisateur de se connecter". Vous écrivez du code de test unitaire qui crée d'abord une instance de la classe Login (que vous n'avez pas encore créée). Vous écrivez ensuite du code pour appeler une méthode dans la classe Login qui transmet un nom d'utilisateur et un mot de passe vides. Enfin, vous écrivez une vérification par rapport au résultat attendu, en vérifiant que l'utilisateur avec une connexion vide n'est réellement pas connecté. Vous essayez d'exécuter un test, mais il ne se compile même pas car vous n'avez pas de classe Login. Vous corrigez cette situation et créez une classe de connexion avec une méthode dans cette classe pour vous connecter et une autre pour vérifier le statut de l'utilisateur pour voir s'il est connecté. Jusqu'à présent, vous n'avez pas implémenté les fonctionnalités de cette classe et la méthode dont nous avons besoin. Vous exécutez le test à ce stade. Maintenant, il compile, mais échoue immédiatement.
What такое TDD и модульное тестирование [перевод] - 12
Revenez maintenant au code et implémentez la fonctionnalité pour réussir le test. Dans notre cas, cela signifie que nous devrions obtenir le résultat : « l’utilisateur n’est pas connecté ». Vous relancez le test et maintenant il réussit. Passons au prochain test. Imaginons maintenant que vous deviez écrire un test appelé "L'utilisateur est connecté s'il a saisi un nom d'utilisateur et un mot de passe valides". Vous écrivez un test unitaire qui instancie la classe Login et tente de vous connecter avec un nom d'utilisateur et un mot de passe. Dans un test unitaire, vous écrivez une instruction selon laquelle la classe Login doit répondre oui à la question de savoir si l'utilisateur est connecté. Vous exécutez ce nouveau test et bien sûr, il échoue car votre classe Login renvoie toujours que l'utilisateur n'est pas connecté. Vous revenez à votre classe Login et implémentez du code pour vérifier que l'utilisateur est connecté. Dans ce cas, vous devrez trouver comment isoler ce module. Pour l'instant, le moyen le plus simple de procéder est de coder en dur le nom d'utilisateur et le mot de passe que vous avez utilisés dans votre test, et s'ils correspondent, de renvoyer le résultat « l'utilisateur est connecté ». Vous effectuez cette modification, exécutez les deux tests et ils réussissent tous les deux. Passons à la dernière étape : vous regardez le code généré, et cherchez un moyen de le réorganiser et de le simplifier. L'algorithme TDD est donc :
  1. Créé un test.
  2. Nous avons écrit du code pour ce test.
  3. Refactorisé le code.

conclusions

What такое TDD и модульное тестирование [перевод] - 13
Это всё, что я хотел рассказать о модульном тестировании и TDD на этом этапе. На самом деле есть много сложностей, связанных с попытками изолировать модули codeа, поскольку code бывает очень сложный и путанный. Очень мало классов существует в полной изоляции. Вместо этого у них есть зависимости, и эти зависимости имеют зависимости и так далее. Whatбы справиться с такими ситуациями, ветеран TDD использует макеты-пустышки (Mock), которые помогают изолировать отдельные классы, подменяя an objectы в зависимых модулях. Эта статья — лишь обзорное и несколько упрощённое введение в модульное тестирование и TDD, мы не будем вдаваться в подробности о модулях-пустышках и других методах TDD. Идея состоит в том, чтобы дать вам основные концепции и принципы TDD и модульного тестирования, которые, надеюсь, у вас теперь есть. Оригинал — https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/