Python-Mock-Klassenattribut
- Grund, ein Klassenattribut zu verspotten
- Mögliche Lösungen zum Mocken eines Klassenattributs
- Python-Mock-Klassenkonstruktor
Das Hauptziel dieses Artikels besteht darin, zu demonstrieren, wie man ein Klassenattribut mit dem Python-Unit-Testing-Modul unittest
zu Test- und Debugging-Zwecken manipuliert.
Grund, ein Klassenattribut zu verspotten
Das Testen des entwickelten Codes auf Bugs, Fehler und Sonderfälle ist einer der wichtigsten Aspekte bei der Entwicklung einer Anwendung, vor allem, wenn die Anwendung für mehrere Benutzer bestimmt ist.
Mit dem eingebauten Python-Modul unittest
können wir Testfälle durchführen, um die Integrität unseres Codes zu testen. Eines der häufigsten Elemente, das strenge Tests erfordert, sind Klassenattribute.
Das Klassenattribut kann zufällige Eingaben verarbeiten, um unerwartetes Verhalten zu verhindern.
Betrachten Sie den folgenden Code:
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
Stellen Sie sich eine Klasse namens Calculate
vor, die ein Attribut namens value
und eine Methode namens Process
enthält. Innerhalb des Wertes
wird ein Wörterbuch gespeichert, das später je nach Anforderung und Datentyp verarbeitet wird.
Um sicherzustellen, dass das Attribut fast jede Art von Wörterbuch speichern kann und fehlerfrei verarbeitet wird, muss man das Attribut testen, um sicherzustellen, dass die Implementierung fehlerfrei ist und keine Revisionen benötigt.
Mögliche Lösungen zum Mocken eines Klassenattributs
Wir können ein Klassenattribut auf zwei Arten verspotten; mit PropertyMock
und ohne PropertyMock
. Lassen Sie uns jeden von ihnen unten anhand von Beispielcode lernen.
Verwenden Sie PropertyMock
, um ein Klassenattribut zu simulieren
Um ein Attribut zu simulieren, können wir PropertyMock
verwenden, das hauptsächlich als Mock für eine Eigenschaft oder als Deskriptor für eine Klasse gedacht ist.
Es ist erwähnenswert, dass PropertyMock
die Methoden __get__
und __set__
bereitstellt, um den Rückgabewert der Eigenschaft zu ändern, sobald sie abgerufen wurde.
Betrachten Sie den folgenden Code:
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "value", new_callable=PropertyMock)
def test_method(self, mocked_attrib):
mocked_attrib.return_value = 1
# Will pass without any complaints
self.assertEqual(Calculate().value, 1)
print("First Assertion Passed!")
self.assertEqual(Calculate().value, 22) # Will throw an assertion
print("Second Assertion Passed!")
if __name__ == "__main__":
Test().test_method()
Ausgang:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 24, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 20, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
In der Lösung wird eine neue Methode test_method
erstellt, um den Wert von Calculate.value
zu ändern. Es ist wichtig zu beachten, dass eine Funktion mit einem patch.object
verziert wird.
Der Decorator patch
im Modul hilft beim Patchen von Modulen und Attributen auf Klassenebene. Um etwas genauer zu machen, was gepatcht werden soll, verwenden wir patch.object
anstelle von patch
, um die Methode direkt zu patchen.
Im Decorator wird zunächst der Klassenname Calculate
übergeben, der anzeigt, dass das zu patchende Objekt ein Teil von Calculate
ist, wobei der Name des Attributs value
übergeben wird.
Der letzte Parameter ist ein PropertyMock
-Objekt, bei dem wir das value
-Attribut überschreiben, indem wir eine andere Zahl übergeben.
Der allgemeine Ablauf des Programms ist wie folgt:
test_method
wird aufgerufen.value
der KlasseCalculate
wird gepatcht, mit einemnew_callable
zugewiesen, einer Instanz vonPropertyMock
.Calculate.value
wird mit1
überschrieben, indem die Eigenschaftreturn_value
verwendet wird.- Die erste Assertion wird geprüft, wobei
Calculate.value
gleich1
sein muss. - Die zweite Assertion wird geprüft, wobei
Calculate.value
gleich22
sein muss.
Ein Klassenattribut simulieren, ohne PropertyMock
zu verwenden
Wir können es auch ohne PropertyMock
lösen. Es ist nur eine geringfügige Modifikation des obigen Beispiels erforderlich.
Betrachten Sie den folgenden Code:
import unittest
from unittest.mock import patch
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "value", 1)
def test_method(self):
# Will pass without any complaints
self.assertEqual(Calculate().value, 1)
print("First Assertion Passed!")
# Will throw an assertion because "Calculate.value" is now 1
self.assertEqual(Calculate().value, 22)
print("Second Assertion Passed!")
if __name__ == "__main__":
Test().test_method()
Ausgang:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 23, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 19, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion because "Calculate.value" is now 1
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
Anstatt eine Instanz von PropertyMock
an new_callable
zu übergeben, können wir den Wert, mit dem wir gespeichert werden möchten, direkt in Calculate.value
übergeben.
Python-Mock-Klassenkonstruktor
Stellen Sie sicher, dass alle initialisierten Variablen wie beabsichtigt funktionieren und kein unbeabsichtigtes Verhalten zeigen. Es ist auch notwendig, Konstrukteure mit unterschiedlichen Eingaben zu testen, um alle Eckfälle zu reduzieren.
Betrachten Sie den folgenden Code:
class Calculate:
num = 0
dic = {}
def __init__(self, number, dictt):
self.num = number
self.dic = dictt
def Process(self): # An example method
pass
Nehmen wir an, wir wollen den Konstruktor der Klasse Berechnen
testen. Um sicherzustellen, dass die Attribute wie beabsichtigt funktionieren, müssen wir den Konstruktor patchen
und ihn mit verschiedenen Eingaben übergeben, um mögliche Fehler auszumerzen.
Wie können wir das machen? Siehe folgende Lösung.
Verwenden Sie den Decorator patch.object
, um den Konstruktor zu patchen
Wir können den Dekorator patch.object
verwenden, um den Konstruktor zu patchen.
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
num = 0
dic = {}
def __init__(self):
self.num = 1
self.dic = {"11", 2}
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "__new__")
def test_method(self, mocked_calc):
mocked_instance = MagicMock()
mocked_instance.num = 10
mocked_instance.dic = {"22": 3}
mocked_calc.return_value = mocked_instance
self.assertEqual(Calculate().num, 10)
self.assertEqual(Calculate().dic, {"22": 3})
print("First set of Asserts done!")
self.assertEqual(Calculate().num, 1)
self.assertEqual(Calculate().dic, {"11", 2})
print("Second set of Asserts done!")
if __name__ == "__main__":
Test().test_method()
Ausgang:
The first set of Asserts is done!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 37, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 32, in test_method
self.assertEqual(Calculate().num, 1)
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 10 != 1
Es ist erwähnenswert, dass wir statt __init__
__new__
gepatcht haben.
Denn bei der Ausführung von __new__
wird die Instanz einer Klasse erzeugt, während bei __init__
nur die Variablen initialisiert werden. Da wir also eine neue simulierte Instanz erstellen müssen, warum patchen wir dann __new__
statt __init__
?
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