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_methodwird aufgerufen.valueder KlasseCalculatewird gepatcht, mit einemnew_callablezugewiesen, einer Instanz vonPropertyMock.Calculate.valuewird mit1überschrieben, indem die Eigenschaftreturn_valueverwendet wird.- Die erste Assertion wird geprüft, wobei
Calculate.valuegleich1sein muss. - Die zweite Assertion wird geprüft, wobei
Calculate.valuegleich22sein 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