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.py
enden. Ein Beispiel wäretest_calc.py
odercalc_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