Ich bin neu in Python, also entschuldige ich mich, wenn dies eine doppelte oder zu einfache Frage ist. Ich habe eine Koordinatorklasse geschrieben, die zwei andere Klassen aufruft, die die Kafka-Python-Bibliothek verwenden, um Daten von Kafka zu senden/zu lesen. Ich möchte einen Komponententest für meine Koordinatorenklasse schreiben, aber ich habe Probleme herauszufinden, wie ich am besten vorgehen soll. Ich hatte gehofft, dass ich einen alternativen Konstruktor erstellen könnte, in den ich meine verspotteten Objekte übergeben kann, aber dies scheint nicht zu funktionieren, da ich eine Fehlermeldung erhalte, die test_mycoordinator nicht lösen kann. Werde ich diese Klasse falsch testen? Gibt es eine pythonische Möglichkeit, die ich testen sollte?
So sieht meine Testklasse bisher aus:
import unittest
from mock import Mock
from mypackage import mycoordinator
class MyTest(unittest.TestCase):
def setUpModule(self):
# Create a mock producer
producer_attributes = ['__init__', 'run', 'stop']
mock_producer = Mock(name='Producer', spec=producer_attributes)
# Create a mock consumer
consumer_attributes = ['__init__', 'run', 'stop']
data_out = [{u'dataObjectID': u'test1'},
{u'dataObjectID': u'test2'},
{u'dataObjectID': u'test3'}]
mock_consumer = Mock(
name='Consumer', spec=consumer_attributes, return_value=data_out)
self.coor = mycoordinator.test_mycoordinator(mock_producer, mock_consumer)
def test_send_data(self):
# Create some data and send it to the producer
count = 0
while count < 3:
count += 1
testName = 'test' + str(count)
self.coor.sendData(testName , None)
Und hier ist die Klasse, die ich testen möchte:
class MyCoordinator():
def __init__(self):
# Process Command Line Arguments using argparse
...
# Initialize the producer and the consumer
self.myproducer = producer.Producer(self.servers,
self.producer_topic_name)
self.myconsumer = consumer.Consumer(self.servers,
self.consumer_topic_name)
# Constructor used for testing -- DOES NOT WORK
@classmethod
def test_mycoordinator(cls, mock_producer, mock_consumer):
cls.myproducer = mock_producer
cls.myconsumer = mock_consumer
# Send the data to the producer
def sendData(self, data, key):
self.myproducer.run(data, key)
# Receive data from the consumer
def getData(self):
data = self.myconsumer.run()
return data
Es ist nicht erforderlich, einen separaten Konstruktor bereitzustellen. Durch das Verspotten wird Ihr Code gepatcht, um Objekte durch Verspottungen zu ersetzen. Verwenden Sie einfach mock.patch()
decorator für Ihre Testmethoden; Es werden Verweise auf die generierten Mock-Objekte übergeben.
Beide producer.Producer()
und consumer.Consumer()
werden dann verspottet , bevor Sie die Instanz erstellen :
import mock
class MyTest(unittest.TestCase):
@mock.patch('producer.Producer', autospec=True)
@mock.patch('consumer.Consumer', autospec=True)
def test_send_data(self, mock_consumer, mock_producer):
# configure the consumer instance run method
consumer_instance = mock_consumer.return_value
consumer_instance.run.return_value = [
{u'dataObjectID': u'test1'},
{u'dataObjectID': u'test2'},
{u'dataObjectID': u'test3'}]
coor = MyCoordinator()
# Create some data and send it to the producer
for count in range(3):
coor.sendData('test{}'.format(count) , None)
# Now verify that the mocks have been called correctly
mock_producer.assert_has_calls([
mock.Call('test1', None),
mock.Call('test2', None),
mock.Call('test3', None)])
In dem Moment, in dem test_send_data
Aufgerufen wird, ersetzt der Code mock.patch()
die Referenz producer.Producer
Durch ein Scheinobjekt. Ihre Klasse MyCoordinator
verwendet dann diese Scheinobjekte und nicht den tatsächlichen Code. Der Aufruf von producer.Producer()
gibt ein neues Mock-Objekt zurück (dasselbe Objekt, auf das mock_producer.return_value
verweist) usw.
Ich bin davon ausgegangen, dass producer
und consumer
Modulnamen der obersten Ebene sind. Ist dies nicht der Fall, geben Sie den vollständigen Importpfad an. Aus der mock.patch()
Dokumentation:
target sollte ein String in der Form
'package.module.ClassName'
sein. Das Ziel wird importiert und das angegebene Objekt durch das neue Objekt ersetzt. Daher muss das Ziel aus der Umgebung importiert werden können, aus der Siepatch()
aufrufen. Das Ziel wird importiert, wenn die dekorierte Funktion ausgeführt wird, nicht zur Dekorationszeit.