[PYTHON] Wie man eine öffentliche Funktion in Pytest verspottet

Hallo, ist um diese Zeit, dass das "geröstete Grüntee Latte Geschmack" Protein eines bestimmten M ○ Proteins im Verkauf gekauft nicht in Suppe trinken ** wie ein Geschmack von ** Garnelen (was

Übrigens, basierend auf der Richtlinie, dass die API unseres Django-Servers ** 120% im Test abdeckt **, schreiben wir den Testcode jeden Tag ungefähr doppelt so viel wie den Implementierungscode. Unter solchen Umständen werde ich heute kurz vorstellen, "wie die in der Helper-Klasse implementierte öffentliche Funktion über die API getestet wird (und wo sie hängen geblieben ist").

Implementierungsinhalt, den Sie testen möchten

Die folgenden zwei Funktionen wurden implementiert (der Funktionsname usw. ist ein Dummy für die Veröffentlichung, und settings.py und urls.py sind bereits separat festgelegt).

app/views.py


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


class HogehogeListAPIView(APIView):
    """Implementieren Sie die Post-Funktionalität in der API-Ansicht mithilfe des Django-Frameworks
    """
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, format=None):
    """ API Post/hogehoge
       """
        serializer = HogehogeSerializer(data=request.data)
        if serializer.is_valid(): #Anforderungsdaten überprüfen
            try:
                with transaction.atomic():
                    serializer.save()
                    helper_foo(serializer.data) #Rufen Sie die Hilfsfunktion auf
            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):
    """Hilfsfunktion
Erstellen Sie einen S3-Bucket basierend auf den Daten
    """
    client = boto3.client("s")
    
    """
Erstellen Sie einen Bucket (werfen Sie einen Fehler, wenn er fehlschlägt)
    """
  client.create_bucket(...

Und hier ist der Inhalt von 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 = #Erstellen Sie Testdaten

    def test_hogehoge_post(self):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Überprüfen Sie mit verschiedenen Asserts

Testprobleme

Der Code, der das S3Bucket tatsächlich erstellt, wird im Test ausgeführt, und das zusätzliche S3Bucket erhöht sich jedes Mal, wenn der Test ausgeführt wird.

Verspotten Sie die Helferfunktion mit einer Gegenmaßnahme (Patch)

Wird ausgeführt, indem die Funktion wie folgt mit einem Patch im Mock-Modul von Unittest verspottet werden kann

Ziel gibt app.helper.foo_helper an.

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)
        #Überprüfen Sie mit verschiedenen Asserts
        self.assertEqual(mock_function.call_count, 1) #Überprüfen Sie, ob die Mock-Funktion einmal aufgerufen wurde

Bei der Ausführung schlägt das Testergebnis fehl und die folgende Meldung wird angezeigt

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

Oh, ist die Funktion nicht verspottet? Wenn Sie die AWS-Konsole überprüfen, ist S3Bucket (leider) gut gemacht.

Was ist passiert

Nach der Untersuchung verschiedener Dinge lautet die Referenzlogik von Patch wie folgt.

Jetzt wollen wir some_function testen, aber SomeClass mit patch () verspotten. Das Problem ist, dass beim Importieren von Modul b, was wir tun müssen, SomeClass aus Modul a importiert wird. Wenn wir patch () zum Verspotten verwenden aus a.SomeClass dann hat es keine Auswirkung auf unseren Test; Modul b hat bereits einen Verweis auf die echte SomeClass und es sieht so aus, als ob unser Patching keine Auswirkung hatte. (Quelle 3 / library / unittest.mock.html # where-to-patch))

Mit anderen Worten Wenn Modul b in Modul a importiert wird, wird Modul b von Modul a referenziert, sodass die Referenzierung mit b.someclass (helper.helper_foo hier) den Test nicht beeinflusst. Und das

Also habe ich das Mock-Ziel von "app.helper.foo_helper" in "app.views.foo_helper" geändert.

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)
        #Überprüfen Sie mit verschiedenen Asserts
        self.assertEqual(mock_function.call_count, 1) #Überprüfen Sie, ob die Mock-Funktion einmal aufgerufen wurde

Dies bestätigte, dass der Test funktionierte und kein S3-Bucket generiert wurde.

Ich benutze Python seit fast 10 Jahren, aber zum ersten Mal konnte ich den Mechanismus der Modulreferenz während des Imports verstehen.

das ist alles. Testcode ist in Bereichen interessant, in denen es verschiedene Entdeckungen gibt, daher hoffe ich, dass Qiita in diesen Tagen um weitere Pytest-Artikel erweitert wird.

Recommended Posts

Wie man eine öffentliche Funktion in Pytest verspottet
So rufen Sie eine Funktion auf
So erstellen Sie eine rekursive Funktion
So löschen Sie einen Taple in einer Liste (Python)
Einbetten von Variablen in Python-Strings
So erstellen Sie ein Funktionsobjekt aus einer Zeichenfolge
So erstellen Sie eine JSON-Datei in Python
So implementieren Sie eine Verlaufsauswahl in Houdini
So benachrichtigen Sie Discord-Kanäle in Python
[Python] Wie zeichnet man mit Matplotlib ein Histogramm?
Wie schreibe ich ein benanntes Tupeldokument im Jahr 2020?
So zählen Sie Zahlen in einem bestimmten Bereich
[Go] So schreiben oder rufen Sie eine Funktion auf
So lesen Sie Dateien in verschiedenen Verzeichnissen
So richten Sie die Authentifizierung mit öffentlichem Schlüssel mit ssh ein
So konvertieren / wiederherstellen Sie einen String mit [] in Python
So definieren Sie Decorator und Decomaker mit einer Funktion
[Python] So erweitern Sie Variablen in einer Zeichenfolge
Ein Memorandum zur Verwendung von Keras 'keras.preprocessing.image
So installieren Sie Google Test / Google Mock in Visual Studio 2019
So verweisen Sie auf statische Dateien in einem Django-Projekt
[Python] So rufen Sie eine Funktion von c aus Python auf (ctypes edition)
[Linux] Wie Sie Ihre IP in eine Variable einfügen
Covector, um in Funktion zu denken
Erstellen Sie eine Funktion in Python
Wie man ein Terminal hackt
Wie man in Python entwickelt
So testen Sie eine Funktion, die die aktuelle Zeit enthält, mit Freezegun in Python
So importieren Sie NoteBook als Modul in Jupyter (IPython)
So geben Sie ein Dokument im PDF-Format mit Sphinx aus
Eine Geschichte darüber, wie man einen relativen Pfad in Python angibt.
Verwendung der Methode __call__ in der Python-Klasse
So importieren Sie Dateien in Python an eine beliebige Stelle
So geben Sie das öffentliche Verzeichnis Python Simple HTTP Server an
So drucken Sie Zeichen als Tabelle mit der Druckfunktion von Python
So generieren Sie einen öffentlichen Schlüssel aus einem privaten SSH-Schlüssel
Praktische Funktion zum Hinzufügen von Spalten an einer beliebigen Stelle in Pandas DataFrame
Hinweise zum Laden einer virtuellen Umgebung mit PyCharm
Ich habe versucht "Wie man eine Methode in Python dekoriert"
So generieren Sie eine Abfrage mit dem IN-Operator in Django
Wie man aus einer Wahrscheinlichkeitsdichtefunktion in Python tastet
So geben Sie char * in einer Rückruffunktion mit ctypes in Python zurück
So erhalten Sie eine Liste der integrierten Ausnahmen für Python
Erstellen wir eine Funktion, um die Schaltfläche in Tkinter gedrückt zu halten
So importieren Sie NoteBook als Modul in Jupyter (IPython)
Wie erstelle ich eine japanisch-englische Übersetzung?
[Python] Wie man PCA mit Python macht
Verwendung der Zip-Funktion
So setzen Sie einen symbolischen Link
Verwendung von Klassen in Theano
Wie man nüchtern mit Pandas schreibt
So sammeln Sie Bilder in Python
Mock in Python-Wie man Mox benutzt
So aktualisieren Sie Spyder in Anaconda
Verwendung von SQLite in Python
So erstellen Sie ein Conda-Paket
Wie erstelle ich einen Crawler?
So erstellen Sie eine virtuelle Brücke