Le guide du débutant pour le développement piloté par les tests

Tester votre code est agaçant, mais ne pas le faire peut avoir des conséquences beaucoup plus graves! Dans cet article, nous utiliserons le développement piloté par les tests pour écrire et tester notre code plus efficacement..


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

Depuis le début de l'ère informatique, les programmeurs et les bugs se sont battus pour la suprématie. C'est un événement inévitable. Même les plus grands programmeurs sont en proie à ces anomalies. Aucun code n'est sécurisé. C'est pourquoi nous faisons des tests. Les programmeurs, au moins sains d'esprit, testent leur code en l'exécutant sur des machines de développement pour s'assurer qu'il fait ce qu'il est supposé faire..


Programmeur sain qui teste ses programmes.
Image reproduite avec l'aimable autorisation de http://www.youthedesigner.com
Programmeur fou qui ne teste pas ses programmes.
Image reproduite avec l'aimable autorisation de http://www.internetannoyanceday.com

Développement piloté par les tests est une technique de programmation qui nécessite l’écriture simultanée du code réel et du code de test automatisé. Cela garantit que vous testez votre code et vous permet de retester votre code rapidement et facilement, car il est automatisé..

Comment ça marche?

Le développement piloté par les tests, ou TDD comme nous l'appellerons désormais, s'articule autour d'un cycle de développement itératif court qui ressemble à ceci:

  1. Avant d'écrire un code, vous devez d'abord rédiger un test automatisé pour votre code. Lors de l'écriture des tests automatisés, vous devez prendre en compte toutes les entrées, erreurs et sorties possibles. De cette façon, votre esprit n'est assombri par aucun code déjà écrit.
  2. La première fois que vous exécutez votre test automatisé, le test doit échouer, indiquant que le code n'est pas encore prêt..
  3. Ensuite, vous pouvez commencer à programmer. Puisqu'il existe déjà un test automatisé, tant que le code échoue, cela signifie qu'il n'est toujours pas prêt. Le code peut être corrigé jusqu'à ce qu'il passe toutes les assertions.
  4. Une fois que le code a réussi le test, vous pouvez alors commencer à le nettoyer, via la refactorisation. Tant que le code réussit toujours le test, cela signifie qu'il fonctionne toujours. Vous n'avez plus à vous soucier des changements qui introduisent de nouveaux bogues.
  5. Recommencez l'opération avec une autre méthode ou un autre programme.

Le cycle de développement piloté par les tests
Image reproduite avec l'aimable autorisation de http://en.wikipedia.org/wiki/Test-driven_development

Génial, mais comment est-ce meilleur que les tests réguliers?

Avez-vous déjà délibérément omis de tester un programme pour les raisons suivantes:

  • Vous pensiez que c'était une perte de temps à tester, puisqu'il ne s'agissait que d'un léger changement de code?
  • Vous vous êtes senti paresseux tout tester à nouveau?
  • Vous n'avez pas eu le temps de tester car le chef de projet souhaitait que la production passe en production au plus vite?
  • Vous vous êtes dit que vous le feriez "demain"?
  • Vous deviez choisir entre des tests manuels ou regarder le dernier épisode de votre émission préférée (Big Bang Theory).?

La plupart du temps, rien ne se passe et vous réussissez à passer votre code à la production sans aucun problème. Mais parfois, une fois que vous êtes passé à la production, tout va mal. Vous êtes coincé à réparer une centaine de trous dans un bateau en train de couler, et de plus en plus apparaissent chaque minute. Tu fais ne pas vouloir te retrouver dans cette situation.


Vissez-le, déplacez-le simplement en production!
Image fournie par http://phenomenaonbreak.wordpress.com

TDD était censé éliminer nos excuses. Lorsqu'un programme utilisant TDD a été développé, il nous permet d'apporter des modifications et de tester rapidement et efficacement. Tout ce que nous avons à faire est de lancer les tests automatisés, et le tour est joué! Si tous les tests automatisés réussissent, nous pouvons continuer, sinon, cela signifie simplement que nous avons rompu quelque chose avec les modifications. En sachant quelles parties exactes du test ont échoué, cela nous permet également de déterminer facilement quelle partie des modifications il a rompues, ce qui facilite la correction des bogues.


Je suis vendu. Comment faisons-nous cela?

Il existe une multitude de cadres de tests automatisés PHP que nous pouvons utiliser. PHPUnit est l’un des frameworks de test les plus utilisés..

PHPUnit est un excellent framework de test, qui peut facilement être intégré dans vos propres projets, ou d'autres projets construits sur des frameworks PHP populaires..

Pour nos besoins cependant, nous n’aurons pas besoin de la multitude de fonctions offertes par PHPUnit. Au lieu de cela, nous choisirons de créer nos tests en utilisant un framework de test beaucoup plus simple, appelé SimpleTest..

Dans les étapes suivantes, supposons que nous développons une application de livre d'or où tout utilisateur peut ajouter et afficher des entrées de livre d'or. Supposons que le balisage est terminé et que nous créons simplement une classe qui contient le logique d'application du livre d'or, à l'endroit où l'application insère et lit dans la base de données. La partie lecture de cette classe est ce que nous allons développer et tester.


Étape 1. Configurez SimpleTest

C'est sans doute l'étape la plus facile de toutes. Même ce gars pourrait le faire:


Je peux le faire? Je peux utiliser, mon, euh? cerveau!
Image reproduite avec l'aimable autorisation de http://longstreet.typepad.com/

Téléchargez SimpleTest ici et extrayez-le dans un dossier de votre choix - de préférence le dossier dans lequel vous allez développer votre code, ou votre chemin include_path PHP pour un accès facile.

Pour ce tutoriel, j'ai configuré le dossier comme suit:

Index.php lancera guestbook.php, invoquera la méthode view et affichera les entrées. Dans le dossier classes, nous placerons la classe guestbook.php, et dans le dossier test, nous placerons la bibliothèque la plus simple..


Étape 2. Planifiez votre attaque


Image reproduite avec l'aimable autorisation de http://connections.smsd.org/veterans

La deuxième étape, qui est en fait la plus importante, consiste à créer vos tests. Pour cela, vous devez vraiment planifier et réfléchir à ce que votre fonction fera, aux entrées possibles qu’elle obtiendra et aux sorties correspondantes qu’elle enverra. Cette étape ressemble à une partie d'échecs. Vous devez tout savoir sur votre adversaire (le programme), y compris toutes ses faiblesses (erreurs possibles) et ses forces (que se passe-t-il si le programme fonctionne correctement).

Donc, pour notre application guestbook, établissons les schémas suivants:

Vue

  • Cette fonction n'aura aucune entrée puisqu'elle récupérera simplement toutes les entrées de la base de données et renverra les données à imprimer..
  • Il renverra une série d'enregistrements de livre d'or, indiquant le nom de l'affiche et son message. S'il n'y a pas d'enregistrements, alors il devrait toujours retourner un tableau vide.
  • S'il y a des enregistrements, le tableau aura 1 ou plusieurs valeurs.
  • Dans le même temps, le tableau aura une structure spécifique, quelque chose comme:
 Array ([0] => Array (['name'] = "Bob" ['message'] = "Bonjour, je suis Bob.") [1] => Array (['name'] = "Tom" ['message'] = "Bonjour, je suis Tom."))

Étape 3. Ecrivez un test!


Image reproduite avec l'aimable autorisation de http://cflhomeless.wordpress.com

Maintenant, nous pouvons écrire notre premier test. Commençons par créer un fichier appelé guestbook_test.php dans le dossier de test.

  

Ensuite, convertissons ce que nous avons déterminé à l'étape deux,.

 ajouter ("Bob", "Bonjour, je suis Bob."); $ guestbook-> add ("Tom", "Bonjour, je suis Tom."); $ entry = $ guestbook-> viewAll (); $ count_is_greater_than_zero = (count ($ entrées)> 0); $ this-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ entrées, 'tableau'); foreach ($ entrées en tant que $ entry) $ this-> assertIsA ($ entry, 'array'); $ this-> assertTrue (isset ($ entry ['name']))); $ this-> assertTrue (isset ($ entry ['message'])));  function testViewGuestbookWithNoEntries () $ guestbook = new Guestbook (); $ guestbook-> deleteAll (); // Supprime d'abord toutes les entrées pour que nous sachions qu'il s'agit d'une table vide $ entries = $ guestbook-> viewAll (); $ this-> assertEqual ($ entrées, array ()); 

Les assertions permettent de s’assurer qu’une certaine chose est ce qu’elle est supposée être. En gros, elle garantit que le retour est ce que vous attendez. Par exemple, si une fonction est supposée retourner vrai si elle réussit, alors dans notre test, nous devrions affirmer que la valeur de retour est égale à true.

Comme vous pouvez le voir ici, nous testons l'affichage du livre d'or avec des entrées et sans. Nous vérifions si ces deux scénarios satisfont nos critères de la deuxième étape. Vous avez probablement aussi remarqué que chacune de nos fonctions de test commence par le mot «test». Nous l'avons fait parce que, lorsque SimpleTest exécute cette classe, toutes les fonctions commençant par le mot 'test' seront recherchées et exécutées..

Dans notre classe de test, nous avons également utilisé certaines méthodes d'assertion, telles que assertTrue, assertIsA et assertEquals. La fonction assertTrue vérifie si une valeur est vraie ou non. AssertIsA vérifie si une variable est d'un certain type ou d'une certaine classe. Et enfin, assertEquals vérifie si une variable est totalement égale à une certaine valeur.

Il existe d'autres méthodes d'assertion fournies par SimpleTest, à savoir:

assertTrue ($ x) Échec si $ x est faux
assertFalse ($ x) Échec si $ x est vrai
assertNull ($ x) Échec si $ x est défini
assertNotNull ($ x) Échec si $ x n'est pas défini
assertIsA ($ x, $ t) Echec si $ x n'est pas la classe ou le type $ t
assertNotA ($ x, $ t) Échec si $ x appartient à la classe ou au type $ t
assertEqual ($ x, $ y) Échec si $ x == $ y est faux
assertNotEqual ($ x, $ y) Échec si $ x == $ y est vrai
assertWithinMargin ($ x, $ y, $ m) Échec si abs ($ x - $ y) < $m is false
assertOutsideMargin ($ x, $ y, $ m) Échec si abs ($ x - $ y) < $m is true
assertIdentical ($ x, $ y) Échec si $ x == $ y est faux ou si le type ne correspond pas
assertNotIdentical ($ x, $ y) Échec si $ x == $ y est vrai et que les types correspondent
assertReference ($ x, $ y) Échec sauf si $ x et $ y sont la même variable
assertClone ($ x, $ y) Échec sauf si $ x et $ y sont des copies identiques
assertPattern ($ p, $ x) Échec sauf si la regex $ p correspond à $ x
assertNoPattern ($ p, $ x) Échec si la regex $ p correspond à $ x
expectError ($ x) Avale toute erreur de correspondance à venir
affirmer ($ e) Échec sur l'objet d'attente attendu échoué $ e

La liste des méthodes d'assertion est une gracieuseté de http://www.simpletest.org/en/unit_test_documentation.html


Étape 4. Échec de la victoire


Image reproduite avec l'aimable autorisation de http://verydemotivational.com

Une fois que vous avez fini d'écrire le code, vous devriez lancer le test. La première fois que vous exécutez le test, il Doit échouer. Si ce n'est pas le cas, cela signifie que votre test ne teste rien.

Pour lancer votre test, lancez simplement guestbook_test.php dans votre navigateur. Vous devriez voir ceci en premier:

Ceci est dû au fait que nous n’avons pas encore créé notre cours de livre d’or. Pour ce faire, créez guestbook.php dans votre dossier de classes. La classe devrait contenir les méthodes que nous prévoyons d’utiliser, mais ne devrait encore rien contenir au début. Rappelez-vous, nous écrivons les tests d'abord avant écrire n'importe quel code.

  

Lorsque vous relancez le test, il devrait ressembler à ceci:

Comme nous pouvons le voir ici, notre test gagne maintenant en échouant. Cela signifie que notre test est maintenant prêt à recevoir une "réponse".


Image reproduite avec l'aimable autorisation de http://www.gamercastnetwork.com/forums

Étape 5. Répondez à votre test en écrivant du code


À un moment donné, nous avons tous ressenti cela lorsque nous programmons.
Image reproduite avec l'aimable autorisation de http://fermentation.typepad.com/fermentation

Maintenant que le test automatisé fonctionne, nous pouvons commencer à écrire du code. Ouvrez votre guestbook.php classe et commence à créer la réponse à ton test.

  'Kirk', 'message' => 'Bonjour, je suis Kirk.' ), array ('name' => 'Ted', 'message' => 'Bonjour, je suis Ted.')); public function viewAll () // Ici, nous devrions récupérer tous les enregistrements de la base de données. // Ceci est simulé en retournant le tableau $ _entries return self :: $ _ entrées;  public function add ($ name, $ message) // Ici, nous simulons une insertion dans la base de données en ajoutant un nouvel enregistrement dans le tableau $ _entries // Voici comment procéder: self :: $ _ entrées [] = array ('name' => $ name, 'message' => $ message); self :: $ _ entries [] = array ('notname' => $ name, 'notmessage' => $ message); // oups, il y a un bug ici quelque part return true;  public function deleteAll () // Nous venons de définir le tableau $ _entries pour simuler self :: $ _ entries = array (); retourne vrai; 

Cette classe guestbook.php a volontairement des bugs, nous pouvons donc voir à quoi ça ressemble si notre test échoue.

Une fois notre test effectué, nous devrions voir quelque chose comme ceci:

La sortie de test nous montre dans quel test et dans quelle assertion notre code a échoué. De là, on peut facilement identifier les lignes 16 et 17 était l’affirmation qui a jeté l’erreur.

 assertTrue (isset ($ entry ['nom']])); $ this-> assertTrue (isset ($ entry ['message']))) ;? 

Cela nous indique clairement que le tableau d'entrées renvoyé n'avait pas la clé de tableau correcte. Sur cette base, nous saurons facilement quelle partie de notre code a mal tourné.

  $ name, 'message' => $ message); //fixé! retourne vrai; ? 

Maintenant, quand nous relancerons notre test, il devrait nous montrer:


Étape 6. Refactoriser et affiner votre code


Images reproduites avec l'aimable autorisation de http://www.osborneink.com et http://phuketnews.phuketindex.com

Comme le code que nous testons ici est assez simple, nos tests et la correction des bogues n'ont pas duré très longtemps. Mais s'il s'agissait d'une application plus complexe, vous auriez dû apporter plusieurs modifications à votre code, le rendre plus propre afin qu'il soit plus facile à gérer, et bien d'autres choses encore. Le problème avec ceci, cependant, est que le changement introduit généralement des bogues supplémentaires. C'est là qu'intervient notre test automatisé. Une fois les modifications apportées, nous pouvons simplement les réexécuter. Si ça passe toujours, alors ça veut dire qu'on n'a rien cassé. Si cela échoue, nous savons que nous avons commis une erreur. Cela nous indique également où se trouve le problème et, espérons-le, comment nous pourrons le résoudre..


Étape 7. Rincer et répéter


Image reproduite avec l'aimable autorisation de http://www.philstockworld.com

Finalement, lorsque votre programme nécessite de nouvelles fonctionnalités, vous devrez écrire de nouveaux tests. C'est facile! Rincez et répétez les procédures à partir de l'étape 2 (car vos fichiers SimpleTest doivent déjà être configurés) et recommencez le cycle..


Conclusion

Il y a beaucoup plus d'articles de développement fondés sur les tests et beaucoup plus de fonctionnalités dans SimpleTest que ce qui était affiché dans cet article, comme des objets fictifs, des stubs, qui facilitent la création de tests. Si vous souhaitez en savoir plus, la page de développement pilotée par les tests de Wikipedia devrait vous mettre sur la bonne voie. Si vous souhaitez utiliser SimpleTest comme infrastructure de test, parcourez la documentation en ligne et assurez-vous de revoir ses autres fonctionnalités..

Les tests font partie intégrante du cycle de développement, cependant, il est trop souvent la première chose à couper lorsque les délais sont imminents. J'espère qu'après avoir lu cet article, vous comprendrez à quel point il est utile d'investir dans un développement piloté par des tests..

Quelles sont vos pensées sur le développement piloté par les tests? Est-ce quelque chose que vous souhaitez mettre en œuvre ou pensez-vous que c'est une perte de temps? Faites-moi savoir dans les commentaires!