[PYTHON] Comment se moquer d'une fonction publique dans Pytest

Bonjour, c'est à peu près à cette époque que la protéine «goût de thé vert torréfié latte» d'une certaine protéine M ○ achetée en solde ne se boit pas à la soupe ** comme un goût de ** crevette (quoi

D'ailleurs, sur la base de la politique selon laquelle l'API de notre serveur Django ** couvre 120% dans le test **, nous écrivons le code de test environ deux fois plus que le code d'implémentation chaque jour. Dans de telles circonstances, aujourd'hui je présenterai brièvement "comment tester la fonction publique implémentée dans la classe Helper via l'API (et où elle s'est bloquée".

Contenu d'implémentation que vous souhaitez tester

Les deux fonctions suivantes ont été implémentées (le nom de la fonction, etc. est factice pour la publication, et setting.py et urls.py sont déjà définis séparément)

app/views.py


from rest_framework.views import APIView
from app.helper import helper_foo
from app.models import HogehogeSerializer


class HogehogeListAPIView(APIView):
    """Implémenter la fonctionnalité de publication dans la vue API à l'aide du framework Django
    """
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, format=None):
    """ API Post/hogehoge
       """
        serializer = HogehogeSerializer(data=request.data)
        if serializer.is_valid(): #Vérification des données de demande
            try:
                with transaction.atomic():
                    serializer.save()
                    helper_foo(serializer.data) #Fonction d'assistance d'appel
            except Exception as error: 
                return Response(status=status.HTTP_400_BAD_REQUEST)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

app/helper.py


import boto3

def helper_foo(data):
    """Fonction d'assistance
Créer un compartiment S3 basé sur les données
    """
    client = boto3.client("s")
    
    """
Créer un bucket (lancer une erreur en cas d'échec)
    """
  client.create_bucket(...

Et voici le contenu de pytest

app/tests.py


from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.data = #Créer des données de test

    def test_hogehoge_post(self):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Vérifier avec diverses assertions

Problèmes de test

Le code qui crée réellement le S3Bucket est exécuté dans le test, et le S3Bucket supplémentaire augmente chaque fois que le test est exécuté.

Simulez la fonction Helper avec une contre-mesure (Patch)

Exécuté en pouvant simuler la fonction comme suit avec un patch dans le module Mock d'Unittest

La cible spécifie ʻapp.helper.foo_helper`.

app/tests.py


from unittest.mock import patch
from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch('app.helper.helper_foo')
    def test_hogehoge_post(self, mock_function):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Vérifier avec diverses assertions
        self.assertEqual(mock_function.call_count, 1) #Vérifiez si la fonction Mock a été appelée une fois

Une fois exécuté, le résultat du test échoue et le message suivant s'affiche

self.assertEqual(mock_function.call_count, 1)
AssertionError: 0 != 1

Oh, la fonction n'est-elle pas moquée? En regardant la console AWS, S3Bucket est (malheureusement) bien fait.

Qu'est-il arrivé

Après avoir étudié diverses choses, la logique de référence de Patch est la suivante.

Maintenant, nous voulons tester some_function mais nous voulons simuler SomeClass en utilisant patch (). Le problème est que lorsque nous importons le module b, ce que nous devrons faire, alors il importe SomeClass du module a. Si nous utilisons patch () pour simuler out a.SomeClass alors cela n'aura aucun effet sur notre test; le module b a déjà une référence à la vraie SomeClass et il semble que notre patch n'a eu aucun effet. (Source 3 / library / unittest.mock.html # where-to-patch))

En d'autres termes Lorsque le module b est importé dans le module a, le module b est référencé à partir du module a, donc le référencer avec b.someclass (helper.helper_foo ici) n'affecte pas le test. Et cela

Donc, j'ai changé la cible de Mock de ʻapp.helper.foo_helper en ʻapp.views.foo_helper.

app/tests.py


from unittest.mock import patch
from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch('app.views.helper_foo')
    def test_hogehoge_post(self, mock_function):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Vérifier avec diverses assertions
        self.assertEqual(mock_function.call_count, 1) #Vérifiez si la fonction Mock a été appelée une fois

Cela a confirmé que le test fonctionnait et qu'aucun compartiment S3 n'avait été généré.

J'utilise Python depuis près de 10 ans, mais pour la première fois, j'ai pu comprendre le fonctionnement de la référence de module lors de l'importation.

c'est tout. Le code de test est intéressant dans les domaines où il y a diverses découvertes, j'espère donc que plus d'articles Pytest seront ajoutés à Qiita ces jours-ci.

Recommended Posts

Comment se moquer d'une fonction publique dans Pytest
Comment appeler une fonction
Comment créer une fonction récursive
Comment effacer un taple dans une liste (Python)
Comment incorporer des variables dans des chaînes python
Comment créer un objet fonction à partir d'une chaîne
Comment créer un fichier JSON en Python
Comment implémenter un sélecteur de dégradé dans Houdini
Comment notifier les canaux Discord en Python
[Python] Comment dessiner un histogramme avec Matplotlib
Comment écrire un document tuple nommé en 2020
Comment compter les nombres dans une plage spécifique
[Go] Comment écrire ou appeler une fonction
Comment lire des fichiers dans différents répertoires
Comment configurer l'authentification par clé publique avec SSH
Comment convertir / restaurer une chaîne avec [] en python
Comment définir Decorator et Decomaker avec une seule fonction
[Python] Comment développer des variables dans une chaîne de caractères
Un mémorandum sur l'utilisation de keras.preprocessing.image de Keras
Comment installer Google Test / Google Mock dans Visual Studio 2019
Comment référencer des fichiers statiques dans un projet Django
[Python] Comment appeler une fonction de c depuis python (édition ctypes)
[Linux] Comment mettre votre IP dans une variable
Covector pour penser en fonction
Créer une fonction en Python
Comment pirater un terminal
Comment développer en Python
Comment tester unitaire une fonction contenant l'heure actuelle à l'aide de Freezegun en Python
Comment importer NoteBook en tant que module dans Jupyter (IPython)
Comment sortir un document au format pdf avec Sphinx
Une histoire sur la façon de spécifier un chemin relatif en python.
Comment utiliser la méthode __call__ dans la classe Python
Comment importer des fichiers où vous le souhaitez en Python
Comment spécifier un serveur HTTP simple Python de répertoire public
Comment imprimer des caractères sous forme de tableau avec la fonction d'impression de Python
Comment générer une clé publique à partir d'une clé privée SSH
Fonction pratique pour ajouter des colonnes n'importe où dans Pandas DataFrame
Remarques sur la façon de charger un environnement virtuel avec PyCharm
J'ai essayé "Comment obtenir une méthode décorée en Python"
Comment générer une requête à l'aide de l'opérateur IN dans Django
Comment échantillonner à partir de n'importe quelle fonction de densité de probabilité en Python
Pour renvoyer char * dans une fonction de rappel à l'aide de ctypes en Python
Comment obtenir une liste d'exceptions intégrées pour python
Créons une fonction pour maintenir Button dans Tkinter
Comment importer NoteBook en tant que module dans Jupyter (IPython)
Comment faire une traduction japonais-anglais
[Python] Comment faire PCA avec Python
Comment utiliser la fonction zip
Comment mettre un lien symbolique
Comment utiliser les classes dans Theano
Comment écrire sobrement avec des pandas
Comment collecter des images en Python
Mock in python - Comment utiliser mox
Comment mettre à jour Spyder dans Anaconda
Comment utiliser SQLite en Python
Comment créer un package Conda
Comment créer un robot - Avancé
Comment créer un pont virtuel