Comment utiliser des fonctions comme des mocks dans les tests de Python
Une des questions les plus souvent posées concernant les tests est : Comment puis-je éviter d’appeler des services lourds pendant l’exécution des tests ?
Si j’avais appelé des services tels que Redis, Docker, MySQL, RabbitMQ ou d’autres pour chaque test, j’aurais probablement dû les installer sur mon serveur de tests. Cela aurait consommé des ressources et augmenté les temps d’exécution. Je pense que les développeurs n’auraient pas aimé cela.
Aussi, c’est utile pour prévenir l’usage des adresses URL réelles. Nous n’avons pas le contrôle sur ces adresses, elles pourraient ne pas être disponibles à certaines heures.
On peut profiter des mocks de Python pour vérifier, par exemple, que notre code effectue un appel à une adresse URL externe et force l’exécution des cas alternatifs. Comment faire si le service n’est pas disponible ou lorsqu’il renvoie une mauvaise réponse.
Pour le projet de démonstration que je vais présenter, je vais utiliser Poetry comme gestionnaire de packages, la bibliothèque requests
pour appeler une URL externe et pytest
avec unittest
comme plugins de test.
Les exemples de code suivants ont été testés sur Python 3.11.
Utiliser Poetry pour configurer le projet
Poetry est un gestionnaire de packages très simple à utiliser. Tu peux le télécharger sur https://python-poetry.org/ . Voici la liste des dépendances à utiliser.
On a black
, un formateur de code, pytest
et pytest-mock
pour avoir un moyen facile de faire des mocks sur Python, ils vont être dans les dépendances de développement. On n’a pas besoin de les avoir dans notre code déployé en production.
Comme dépendances de production, on va utiliser Python 3.11
et requests
, le plugin pour effectuer plusieurs appels HTTP à différentes URL.
Un script en Python pour appeler une API
Comme script en Python, on va le faire très simplement. Je vais procéder à réaliser les 3 types d’appels les plus utilisés : un appel GET, un appel POST et un appel POST avec des données.
Si j’exécute ce script, il fonctionnera à condition que l’URL de JsonPlaceholder soit disponible.
Utiliser des mocks pour remplacer des fonctions
On va souvent utiliser la méthode patch
de unittest.mock
. Avec ce décorateur, on va substituer la méthode que l’on souhaite et en faire un mock. Dans ce processus, elle obtiendra de multiples fonctionnalités utiles qui nous permettront de savoir si elle a été appelée, les paramètres utilisés et même de retourner une valeur fausse comme réponse.
La fonction que l’on doit remplacer par un mock dans ce cas est requests.get
. Assure-toi d’importer get
uniquement depuis requests
. On doit mettre l’adresse complète dans la fonction, ainsi que le package inclus.
Les fonctions dans les mocks sont très diverses, et parfois tu peux utiliser assert
ou les assertions, qui sont inclus dans les mocks.
Vérifier si la fonction a été appelée une seule fois, mode décorateur
Pour ce cas, on va utiliser le mot-clé spécial de Python assert
. Le mock est livré avec une fonction qui retourne vrai ou faux, appelée called_once
.
Regardez comment on utilise un décorateur sur notre test test_call_single_endpoint_mock
. De cette manière, on évite d’indenter le code.
Vérifier si la fonction a été appelée une seule fois, en utilisant with
On peut ainsi ne pas utiliser le décorateur, dans ce cas, on ne va pas faire de patch. C’est une ligne additionnelle qu’on doit ajouter. Personnellement, je préfère la version avec décorateur, mais il n’est jamais superflu d’apprendre les manières alternatives.
Vérifier si la fonction a été appelée, en utilisant le paramètre mocker
De plus, on peut utiliser mocker
comme paramètre pour faire un mock de la fonction que tu manques de substituer. Avec cette méthode, on évite une indentation supplémentaire. L’avantage est que avec ce paramètre tu peux faire un mock de plus de fonctions, en évitant l’accumulation des décorateurs.
Vérifier si la fonction a été appelée avec des paramètres spécifiques
On a deux versions de ces assertions. On peut vérifier si une fonction a été appelée avec des paramètres spécifiques. Pour cela, on utilise la fonction du mock assert_called_with
. Il s’agit d’une variation qui s’assure que la fonction a été appelée une seule fois exactement avec assert_called_once_with
.
Groupement des tests
Si on veut un meilleur ordre pour nos tests, on peut les regrouper dans une classe. Tu peux même combiner des tests groupés et non groupés dans le même fichier.
Dans l’exemple suivant, on va spécifier au mock l’objet à retourner. Cela peut être utile si tu veux forcer un flux spécifique pour tester comment tes fonctions (comme mocks) se comportent.
Souviens-toi que dans une classe, le premier paramètre de ses fonctions sera toujours self
. Donc, le deuxième paramètre sera ta fonction comme mock.
Vérifier les appels consécutifs d’une méthode
Dans la fonction mock
, il y a une méthode appelée side_effect
. Cela est utile lorsque ta méthode est appelée plus d’une fois et que tu souhaites spécifier des retours exclusifs.
La méthode side_effect
est également utilisée pour programmer des Exceptions comme réponse.
Vérifier si un appel est similaire à un autre en utilisant call
Si on veut éviter d’utiliser assert_called_with
ou assert_called_once_with
, on peut aussi utiliser la fonction qui se trouve dans unittest
appelée call
. Ce que call fait, c’est retourner un appel à un Mock
ou MagicMock
. Ainsi, on va comparer si les appels à un point spécifique d’exécution sont les mêmes que ceux enregistrés par le mock.
Vérifier si le paramètre n d’une fonction a été appelé avec une valeur spécifique
Ceci est une variation d’un test où l’on souhaite savoir si le paramètre dans une position spécifique d’une fonction a une valeur spécifique. Étant donné que l’on souhaite obtenir les paramètres d’un mock sous forme de liste, il sera très facile d’accéder à l’index exact.
Dans l’exemple suivant, on peut acquérir les paramètres d’un appel à une fonction en utilisant call_args
.
Le résultat final
Ceci est le code final de tous les tests que j’ai faits. Ils ne couvrent pas 100% des cas qu’une application peut avoir, mais j’espère avoir montré les plus utilisés.
Le lien vers le dépôt sera dans la section des Recommandations.
Recommandations
Tu peux voir le dépôt avec le résultat final ici .
Pour plus d’informations sur unittest.mock
, consulte la documentation .