Scheinfunktion in Python
Die Funktion unittest.mock
oder Mock
ist eine Bibliothek zum Testen in Python, mit der Sie Komponenten Ihres zu testenden Systems durch Mock
-Objekte ersetzen und Aussagen darüber treffen können, wie die Teile verwendet wurden.
Die unittest.mock
gibt eine Mock
-Kernklasse, wodurch die Notwendigkeit beseitigt wird, eine Vielzahl von Stubs in Ihrer gesamten Testsuite zu erstellen.
Nach dem Ausführen eines Prozesses können Sie bestätigen, welche Methoden oder Attribute verwendet wurden und mit welchen Argumenten sie aufgerufen wurden. Sie können auch die Rückgabewerte angeben und die benötigten Attribute auf die übliche Weise setzen.
Darüber hinaus bietet Mock
einen patch()
-Dekorator, der das Patching-Modul und die Attribute auf Klassenebene innerhalb des Testumfangs handhaben kann, zusammen mit einem Helfer sentinel
zum Erstellen eindeutiger Objekte.
Mock
wurde für die Verwendung mit unittest
erstellt und basiert auf dem action-to-assertion
-Muster anstelle von record-to-replay
, das in den meisten Mocking-Frameworks verwendet wird. Es gibt eine Rückportierung von unittest.mock
für frühere Versionen von Python.
Einrichten und Installieren von Pytest Mock in Python
Im Gegensatz zu unittest
ist pytest
kein eingebautes Python-Paket und muss installiert werden. Dies kann durch Eingabe des folgenden Befehls im Terminal installiert werden.
pip install pytest
Als Hinweis gelten die Best Practices für unittest
auch für pytest
, so dass:
- Ein
tests/
-Verzeichnis ist erforderlich, um alle Unit-Tests zu enthalten. - Dateinamen müssen immer mit
tests_
beginnen. - Funktionsnamen müssen immer mit
the test
beginnen.
Damit der Prüfer die durchgeführten Unit-Tests finden kann, müssen die Benennungsstandards eingehalten werden.
Die Installation von pytest-mock
ist erforderlich, bevor Sie es verwenden können. So installieren Sie es mit pip
:
pip install pytest-mock
Dies ist ein Plugin für Pytest. Daher wird Pytest installiert, falls Sie dies noch nicht getan haben.
Mock eine Funktion in Python
Erstellen Sie zunächst eine einfache Funktion get_os()
, die uns mitteilt, ob wir Windows oder Linux als Betriebssystem verwenden.
Erstellen Sie eine Datei namens app.py
wie folgt:
from time import sleep
def isWindows():
# This sleep can be some complex operation
sleep(5)
return True
def get_os():
return "Windows is the current OS" if isWindows() else "Linux is the current OS"
Diese Funktion verwendet die Funktion isWindows
, um festzustellen, ob das aktuelle Betriebssystem Windows ist oder nicht. Unter der Annahme, dass diese isWindows
-Funktion ziemlich komplex ist und einige Sekunden zum Ausführen benötigt, können wir hier diese langsame Funktion simulieren, indem wir das Programm bei jedem Aufruf für 5 Sekunden in den Ruhezustand versetzen.
Folgendes wäre ein pytest
für die Funktion get_os()
:
Erstellen Sie eine Datei namens test_app.py
zu pytest
:
from app import get_os
def test_get_os():
assert get_os() == "Windows is the current OS"
Da get_os()
eine langsamere Funktion isWindows
aufruft, verläuft der Test langsam. Dies ist in der folgenden Ausgabe der Ausführung von pytest
zu sehen.
Es dauerte 5,02 Sekunden und kann je nach aktueller Instanz variieren.
Komponententests müssen schnell sein, und wir sollten in der Lage sein, Hunderte von Tests in Sekundenschnelle durchzuführen. Die Testsuite wird durch einen einzelnen Test verlangsamt, der 5 Sekunden dauert, sodass das Anwenden von Mocking unser Leben einfacher macht.
Wenn wir die langsame Funktion patchen, können wir das Verhalten der Funktion get_os()
überprüfen, ohne 5 Sekunden zu verweilen.
Lassen Sie uns diese Funktion mit pytest-mock
verspotten.
Pytest-mock
bietet eine Einrichtung namens mocker
und eine feine Schnittstelle zusätzlich zu Pythons integrierten Mocking-Konstrukten. Sie können einen Mocker verwenden, indem Sie die Mock- und Patch-Funktionen daraus aufrufen und sie als Argumente an Ihre Testfunktion senden.
In einem Fall, in dem Sie möchten, dass die Funktion isWindows
True
zurückgibt, ohne auf diese wertvollen 5 Sekunden zu warten, können wir sie wie folgt patchen:
mocker.patch("app.isWindows", return_value=True)
Hier muss isWindows
als app.isWindows
bezeichnet werden, da es sich um die Funktion im app
-Modul handelt. Wenn wir nur isWindows
patchen, wird es versuchen, eine Funktion namens isWindows
in der Datei test_app
zu patchen, die nicht existiert.
Das Format ist immer <Modulname>.<Funktionsname>
, und es ist wichtig zu wissen, wie man richtig mockt.
Im Folgenden wird die geänderte Testfunktion nach dem Patch beschrieben:
from app import get_os
# using'mocker' fixture provided by pytest-mock
def test_get_os(mocker):
# mocking the slow-going function and returning True always
mocker.patch("app.isWindows", return_value=True)
assert get_os() == "Windows is the current OS"
Dieser Test wird jetzt viel schneller beendet, wenn Sie ihn ausführen.
Wie Sie sehen können, dauerte der Test nur 0,02 Sekunden, also haben wir die langsame Funktion erfolgreich gepatcht und die Testsuite beschleunigt.
Ein weiterer Vorteil des Mockings besteht darin, dass Sie die Mock-Funktion dazu bringen können, alles zurückzugeben und sogar Fehler auszulösen, um zu testen, wie Ihr Code in diesen Szenarien verarbeitet wird.
Wenn Sie hier den Fall testen möchten, in dem isWindows
False
zurückgibt, schreiben Sie den folgenden Test:
from app import get_os
def test_os_isLinux(mocker):
mocker.patch(
"app.isWindows", return_value=False
) # set the return value to be False
assert get_os() == "Linux is the current OS"
Die Mocks und Patches, die mit dem Mocker geliefert werden, sind alle funktionsbezogen, was bedeutet, dass sie nur mit dieser bestimmten Funktion verwendet werden können. Dadurch kommt es zu keinen Konflikten zwischen Patches für dieselbe Funktion in unterschiedlichen Tests.
Abschluss
Mocking ist die Praxis, die Anwendungskomponente, die Sie testen, durch ein Mock zu ersetzen, bei dem es sich um eine Dummy-Implementierung dieser Komponente handelt. Durch Mocking können wir Vorteile erzielen, wie z. B. eine erhöhte Geschwindigkeit, bei der schnellere Tests sehr vorteilhaft sind, das Vermeiden unerwünschter Nebenwirkungen während des Testens und so weiter.
Nimesha is a Full-stack Software Engineer for more than five years, he loves technology, as technology has the power to solve our many problems within just a minute. He have been contributing to various projects over the last 5+ years and working with almost all the so-called 03 tiers(DB, M-Tier, and Client). Recently, he has started working with DevOps technologies such as Azure administration, Kubernetes, Terraform automation, and Bash scripting as well.