Python Unittest vs. Pytest
Das Hauptziel dieses Artikels ist es, zwei der am häufigsten verwendeten Frameworks für Unit-Tests in Python zu diskutieren, unittest und pytest, ihre Vor- und Nachteile und wann man welches dem anderen vorzieht.
Python unittest gegen Pytest
Beim Schreiben von Software müssen wir den Prozess der Fehlerprüfung während des gesamten Entwicklungsprozesses aufrechterhalten. Es stellt sicher, dass, sobald die Software das Release-Stadium erreicht, während ihrer Verwendung eine minimale Anzahl von Fehlern auftritt.
Python verfügt auch über eine Vielzahl von Test-Frameworks, die das Testen von geschriebenem Code ermöglichen, indem ihm verschiedene Eingaben zur Überprüfung seines Verhaltens gegeben werden.
Falls ein Fehler auftritt, kann er während der Entwicklungsphase behoben werden, im Gegensatz zu hotfixes nach der ersten Veröffentlichung der Anwendung.
Beispielcode:
class Calculate:
def CheckPrime(self, a):
for i in range(a):
if a % i:
return False
return True
def CalcFact(self, a):
if a == 1:
return a
else:
return a * self.fact(a - 1)
Der oben gezeigte Code enthält zwei Funktionen namens CheckPrime und CalcFact, die, wie aus ihren Namen ersichtlich ist, nach Primzahlen suchen und Fakultäten berechnen.
Damit die Berechnen-Methoden reibungslos funktionieren, ist die Überprüfung auf Fehler, die durch unterschiedliche Ausgaben auftreten können, unerlässlich.
Also, wie können wir das tun? Um sicherzustellen, dass unser Code fehlerfrei ist, können wir verschiedene Test-Frameworks verwenden, um Testfälle zu schreiben und unseren Code darüber hinaus zu testen, um die Integrität unseres Codes zu überprüfen.
Obwohl es viele Test-Frameworks gibt, sind zwei der am weitesten verbreiteten unittest und pytest. Lassen Sie uns sie unten einzeln untersuchen.
Unit Test durch unittest Framework
unittest ist ein Unit-Testing-Framework, das in der Python-Standardbibliothek enthalten ist. Dieses Framework wurde von JUnit inspiriert, einem Java-Framework für Unit-Tests.
Bevor Sie die Funktionsweise von unittest besprechen, ist es wichtig, häufig verwendete Begriffe in unittest (auch in anderen verwandten Frameworks verwendet) zu kennen.
Test Case– Kleinste Testeinheit – Besteht in der Regel aus einem einzigenTest Suite– Testfälle gruppiert – Üblicherweise nacheinander ausgeführtTest Runner– Koordiniert und verwaltet die Ausführung von Testfällen und Suiten
Verwenden Sie das unittest-Framework, um Testfälle zu schreiben
Da die Standardbibliothek von Python bereits unittest enthält, muss kein externes Modul heruntergeladen werden, um Unit-Tests mit unittest zu schreiben.
Nach dem Import des Moduls unittest kann es losgehen. Konzentrieren wir uns nun auf den Code, den wir zuvor durchgegangen sind.
Beispielcode:
class Calculate:
def CheckPrime(self, a):
for i in range(a):
if a % i:
return False
return True
def CalcFact(self, a):
if a == 1:
return a
else:
return a * self.fact(a - 1)
Um Testfälle mit unittest zu schreiben, müssen wir einer bestimmten Syntax folgen, nämlich dass die Testklasse ein Kind von unittest.TestCase ist und ihre Methoden mit test_ beginnen müssen.
Betrachten Sie den folgenden Code:
import unittest
class Calculate:
def CheckPrime(self, a):
for i in range(2, a):
if a % i == 0:
return False
return True
def CalcFact(self, a):
if a == 1:
return a
else:
return a * self.CalcFact(a - 1)
class TestCalc(unittest.TestCase):
def test_CheckPrime(self):
calc = Calculate()
# Passing different outputs
self.assertEqual(calc.CheckPrime(2), True)
self.assertEqual(calc.CheckPrime(3), True)
self.assertEqual(calc.CheckPrime(4), False)
self.assertEqual(calc.CheckPrime(80), False)
def test_CheckFact(self):
calc = Calculate()
# Passing different outputs
self.assertEqual(calc.CalcFact(2), 2)
self.assertEqual(calc.CalcFact(3), 6)
Ausgang:
PS D:\Unittest> python -m unittest a.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Der Ausgabe nach zu urteilen, können wir sehen, dass alle Testfälle bestanden wurden, weil alle Behauptungen erfolgreich waren.
Versuchen wir nun einen Fall, in dem der Testfall fehlschlägt.
def test_CheckFact(self):
calc = Calculate()
# Passing different outputs
self.assertEqual(calc.CalcFact(2), 2)
self.assertEqual(calc.CalcFact(3), 6)
# Supposed to throw an error
self.assertEqual(calc.CalcFact(0), 0)
Ausgang:
PS D:\Unittest> python -m unittest a.py
======================================================================
ERROR: test_CheckFact (a.TestCalc)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Python Articles\a.py", line 34, in test_CheckFact
self.assertEqual(calc.CalcFact(0), 0) # Supposed to throw an error
File "D:\Python Articles\a.py", line 15, in CalcFact
return a * self.CalcFact(a-1)
File "D:\Python Articles\a.py", line 15, in CalcFact
return a * self.CalcFact(a-1)
File "D:\Python Articles\a.py", line 15, in CalcFact
return a * self.CalcFact(a-1)
[The previous line is repeated 974 more times]
File "D:\Python Articles\a.py", line 12, in CalcFact
if (a == 1):
RecursionError: maximum recursion depth exceeded in comparison
----------------------------------------------------------------------
Ran 2 tests in 0.004s
FAILED (errors=1)
Wie aus dem Code hervorgeht, führen wir das Skript mit python -m unittest <name_of_script.py> aus.
Dieser Code funktioniert, ohne die Methoden der Testklasse aufzurufen, da das unittest-Modul Skriptdateien verarbeitet, die ihm in einem bestimmten Format übergeben werden.
Da unser Skript TestCalc enthielt, wird die Kindklasse von unittest.TestCase automatisch vom Test Runner instanziiert.
Nach der Instanziierung werden Testmethoden innerhalb der Klasse gefunden und der Reihe nach ausgeführt. Damit eine Methode als Testmethode betrachtet werden kann, muss sie mit einem test_ beginnen.
Sobald die Testmethoden gefunden sind, werden sie der Reihe nach aufgerufen; in unserem Fall werden sowohl test_CheckPrime als auch test_CalcFact aufgerufen. Zusicherungen werden in unserer Implementierung überprüft und bei unerwartetem Verhalten wird ein Fehler in die Ausgabe geworfen.
Aus unserem Testfall, der einen Fehler enthielt, kann man ableiten, dass aufgrund der Code-Schreibweise eine unendliche Rekursion in der Methode CalcFact gestartet wurde, die nun dank des Testfalls behoben werden kann.
Falls Sie sich fragen, warum der Fehler auftritt, liegt dies daran, dass die Anfangsbedingung nicht nach Zahlen kleiner als eins sucht.
Vor- und Nachteile des Frameworks unittest
Einige der Vorteile der Verwendung von unittest sind unten aufgeführt:
- In der Python-Standardbibliothek enthalten
- Fördert verwandte Testfälle in einer einzigen Testsuite
- Schnelle Testsammlung
- Präzise Testzeitdauer
Der unittest hat folgende Nachteile:
- Kann schwer zu verstehen sein
- Keine farbige Ausgabe
- Kann zu ausführlich sein
Unit-Test durch das Pytest-Framework
Anders als unittest ist Pytest kein eingebautes Modul; wir müssen es separat herunterladen. Die Installation von Pytest ist jedoch relativ einfach; Dazu können wir pip verwenden und den folgenden Befehl ausführen.
pip install pytest
Verwenden Sie Pytest, um Testfälle zu schreiben
Lassen Sie uns einige Testfälle mit Pytest schreiben. Bevor wir jedoch beginnen, schauen wir uns an, wie sich Pytest von unittest beim Schreiben von Testfällen unterscheidet. Für in Pytest geschriebene Unit-Tests müssen wir:
- Erstellen Sie ein separates Verzeichnis und legen Sie die zu testenden Skripte in das neu erstellte Verzeichnis.
- Schreiben Sie Tests in Dateien, die entweder mit
test_beginnen oder mit_test.pyenden. Ein Beispiel wäretest_calc.pyodercalc_test.py.
Betrachten Sie den folgenden Code, der für Testfälle mit Pytest geschrieben wurde.
def test_CheckPrime():
calc = Calculate()
# Passing different outputs
assert calc.CheckPrime(2) == True
assert calc.CheckPrime(3) == True
assert calc.CheckPrime(4) == False
assert calc.CheckPrime(80) == False
def test_CheckFact():
calc = Calculate()
# Passing different outputs
assert calc.CalcFact(2) == 2
assert calc.CalcFact(3) == 6
# assert calc.CalcFact(0) == 0 # Supposed to throw an error
Ausgang:
============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 items
test_a.py
[100%]
=============================================================== 2 passed in 0.04s ===============================================================
Nun, mit einem fehlgeschlagenen Testfall:
============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 items
test_a.py .F
[100%]
=================================================================== FAILURES ====================================================================
________________________________________________________________ test_CheckFact _________________________________________________________________
def test_CheckFact():
calc = Calculate()
# Passing different outputs
assert calc.CalcFact(2) == 2
assert calc.CalcFact(3) == 6
> assert calc.CalcFact(0) == 0 # Supposed to throw an error
test_a.py:50:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_a.py:13: in CalcFact
return a * self.CalcFact(a-1)
test_a.py:13: in CalcFact
return a * self.CalcFact(a-1)
test_a.py:13: in CalcFact
return a * self.CalcFact(a-1)
.
.
.
.
.
RecursionError: maximum recursion depth exceeded in comparison
test_a.py:10: RecursionError
============================================================ short test summary info ============================================================
FAILED test_a.py::test_CheckFact - RecursionError: maximum recursion depth exceeded in comparison
========================================================== 1 failed, 1 passed in 2.42s ==========================================================
Die mit Pytest geschriebenen Testfälle sind etwas einfacher als unittest; Anstatt eine Klasse zu erstellen, die das Kind von unittest.TestCase war, können wir unsere Testfunktionen einfach mit test_ am Anfang der Methode schreiben.
Vor- und Nachteile des Pytest-Frameworks
Im Folgenden sind einige Vorteile der Verwendung des Pytest-Frameworks in Python aufgeführt.
-
Kompakte Testsuiten
-
Minimaler Boilerplate-Code
-
Plugin-Unterstützung
-
Saubere und korrekte Ausgabepräsentation
Es hat auch einen Nachteil, der unten aufgeführt ist.
-
Oft inkompatibel mit anderen Frameworks
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn