webentwicklung-frage-antwort-db.com.de

Wie verspotte ich einen Teil eines python - Konstruktors nur zum Testen?

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
14
jencoston

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 Sie patch() aufrufen. Das Ziel wird importiert, wenn die dekorierte Funktion ausgeführt wird, nicht zur Dekorationszeit.

21
Martijn Pieters