Une introduction aux tests unitaires en Python

Vous venez de terminer d'écrire un morceau de code et vous vous demandez quoi faire. Soumettez-vous une demande d'extraction et demanderez-vous à vos coéquipiers d'examiner le code? Ou allez-vous tester manuellement le code?

Vous devez faire ces deux choses, mais avec une étape supplémentaire: vous devez tester votre code unitaire pour vous assurer que le code fonctionne comme prévu.

Les tests unitaires peuvent réussir ou échouer, ce qui en fait une excellente technique pour vérifier votre code. Dans ce tutoriel, je vais vous montrer comment écrire des tests unitaires en Python et vous verrez à quel point il est facile de les lancer dans votre propre projet.

Commencer

La meilleure façon de comprendre les tests est de le faire de manière pratique. Pour cela, dans un fichier nommé name_function.py, j'écrirai une fonction simple qui prend un prénom et un nom, et renvoie un nom complet:

#Generate a formatted full name def formatted_name(first_name, last_name): full_name = first_name + ' ' + last_name return full_name.title()

La fonction formatted_name () prend le nom et le prénom et les combine avec un espace entre les deux pour former un nom complet. Il met ensuite en majuscule la première lettre de chaque mot. Pour vérifier que ce code fonctionne, vous devez écrire du code qui utilise cette fonction. Dans names.py, j'écrirai un code simple qui permet aux utilisateurs d'entrer leurs nom et prénom:

from name_function import formatted_name print("Please enter the first and last names or enter x to E[x]it.") while True: first_name = input("Please enter the first name: ") if first_name == "x": print("Good bye.") break last_name = input("Please enter the last name: ") if last_name == "x": print("Good bye.") break result = formatted_name(first_name, last_name) print("Formatted name is: " + result + ".")

Ce code importe formaté_nom () à partir de nom_fonction.py et lors de l'exécution, permet à l'utilisateur d'entrer une série de prénoms et de noms et affiche les noms complets formatés.

Test unitaire et cas de test

Il existe un module dans la bibliothèque standard de Python appelé unittest qui contient des outils pour tester votre code. Les tests unitaires vérifient si toutes les parties spécifiques du comportement de votre fonction sont correctes, ce qui facilitera leur intégration avec d'autres parties.

Le cas de test est un ensemble de tests unitaires qui, ensemble, prouvent qu'une fonction fonctionne comme prévu, dans une gamme complète de situations dans lesquelles cette fonction peut se trouver et qu'elle est censée gérer. Le cas de test doit prendre en compte tous les types d'entrées possibles qu'une fonction peut recevoir des utilisateurs et doit donc inclure des tests pour représenter chacune de ces situations.

Passer un test

Voici un scénario typique pour écrire des tests:

Vous devez d'abord créer un fichier de test. Importez ensuite le module unittest, définissez la classe de test qui hérite de unittest.TestCase et, enfin, écrivez une série de méthodes pour tester tous les cas de comportement de votre fonction.

Il y a une explication ligne par ligne sous le code suivant:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger")

Tout d'abord, vous devez importer un test unitaire et la fonction que vous souhaitez tester, nom_formaté (). Ensuite, vous créez une classe, par exemple NamesTestCase, qui contiendra des tests pour votre fonction formatted_name (). Cette classe hérite de la classe unittest.TestCase.

NamesTestCase contient une seule méthode qui teste une partie de formatted_name (). Vous pouvez appeler cette méthode test_first_last_name ().

N'oubliez pas que chaque méthode commençant par «test_» sera exécutée automatiquement lorsque vous exécuterez test_name_function.py.

Dans la méthode de test test_first_last_name (), vous appelez la fonction que vous souhaitez tester et stocker une valeur de retour. Dans cet exemple, nous allons appeler nom_formaté () avec les arguments «pete» et «seeger», et stocker le résultat dans la variable résultante.

Dans la dernière ligne, nous utiliserons la méthode assert. La méthode assert vérifie qu'un résultat que vous avez reçu correspond au résultat que vous vous attendiez à recevoir. Et dans ce cas, nous savons que la fonction formatted_name () retournera le nom complet avec les premières lettres majuscules, donc nous attendons le résultat «Pete Seeger». Pour vérifier cela, la méthode assertEqual () de l'unittest est utilisée.

self.assertEqual(result, “Pete Seeger”)

Cette ligne signifie essentiellement: Comparez la valeur de la variable résultante avec «Pete Seeger» et si elles sont égales, c'est OK, mais si elles ne le sont pas, faites-le moi savoir.

Lors de l'exécution de test_name_function.py, vous êtes censé obtenir un OK signifiant que le test a réussi.

Ran 1 test in 0.001s OK

Échec d'un test

Pour vous montrer à quoi ressemble un test qui échoue, je vais modifier une fonction formatted_name () en incluant un nouvel argument de deuxième prénom.

Je vais donc réécrire la fonction pour qu'elle ressemble à ceci:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name, middle_name): full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title()

Cette version de formatted_name () fonctionnera pour les personnes avec un deuxième prénom, mais lorsque vous la testerez, vous verrez que la fonction est interrompue pour les personnes qui n'ont pas de deuxième prénom.

Ainsi, lorsque vous exécutez le test_name_function.py, vous obtiendrez le résultat qui ressemble à ceci:

Error Traceback (most recent call last): File “test_name_function.py”, line 7, in test_first_last_name result = formatted_name(“pete”, “seeger”) TypeError: formatted_name() missing 1 required positional argument: ‘middle_name’ Ran 1 test in 0.002s FAILED (errors=1)

Dans la sortie, vous verrez des informations qui vous diront tout ce dont vous avez besoin pour savoir où le test échoue:

  • Le premier élément de la sortie est l'erreur qui vous indique qu'au moins un test dans le cas de test a abouti à une erreur.
  • Ensuite, vous verrez le fichier et la méthode dans lesquels l'erreur s'est produite.
  • Après cela, vous verrez la ligne dans laquelle l'erreur s'est produite.
  • Et quel genre d'erreur il s'agit, dans ce cas, il nous manque 1 argument «middle_name».
  • Vous verrez également le nombre de tests exécutés, le temps nécessaire pour que les tests se terminent et un message textuel qui représente l'état des tests avec le nombre d'erreurs qui se sont produites.

Que faire lorsque le test a échoué

Un test réussi signifie que la fonction se comporte conformément à ce que l'on attend d'elle. Cependant, un test échoué signifie qu'il y a plus de plaisir devant vous.

J'ai vu quelques programmeurs qui préfèrent changer le test au lieu d'améliorer le code - mais ne le faites pas. Passez un peu plus de temps à résoudre le problème, car cela vous aidera à mieux comprendre le code et à gagner du temps à long terme.

In this example, our function formatted_name() first required two  parameters, and now as it is rewritten it requires one extra: a middle name. Adding a middle name to our function broke the desired behavior of  it. Since the idea is not to make changes to the tests, the best solution is to make middle name optional.

After we do this the idea is to make the tests pass when the first and last name are used, for example “Pete Seeger”, as well as when first, last and middle names are used, for example “Raymond Red Reddington”. So  let’s modify the code of formatted_name() once again:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name,): if len(middle_name) > 0: full_name = first_name + ' ' + middle_name + ' ' + last_name else: full_name = first_name + ' ' + last_name return full_name.title()

Now the function should work for names with and without the middle name.

And to make sure it still works with “Pete Seeger” run the test again:

Ran 1 test in 0.001s OK
Et c'est ce que je voulais vous montrer: il est toujours préférable d'apporter des modifications à votre code pour l'adapter à vos tests que l'inverse. Le moment est maintenant venu d'ajouter un nouveau test pour les noms qui ont un deuxième prénom.

Ajout de nouveaux tests

Écrivez une nouvelle méthode dans la classe NamesTestCase qui testera les prénoms:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger") def test_first_last_middle_name(self): result = formatted_name("raymond", "reddington", "red") self.assertEqual(result, "Raymond Red Reddington")

Une fois le test exécuté, les deux tests doivent réussir:

Ran 2 tests in 0.001s OK
Bra gjort!

Bien joué!

Vous avez écrit vos tests pour vérifier si la fonction fonctionne en utilisant des noms avec ou sans deuxième prénom. Restez à l'écoute pour la partie 2 où je parlerai plus en détail des tests en Python.

Merci pour la lecture! Consultez plus d'articles comme celui-ci sur mon profil freeCodeCamp: //www.freecodecamp.org/news/author/goran/ et d'autres choses amusantes que j'ai construites sur ma page GitHub: //github.com/GoranAviani