I am new to Python, so I apologize if this is a duplicate or overly simple question. I have written a coordinator class that calls two other classes that use the kafka-python library to send/read data from Kafka. I want to write a unit test for my coordinator class but I'm having trouble figuring out how to best to go about this. I was hoping that I could make an alternate constructor that I could pass my mocked objects into, but this doesn't seem to be working as I get an error that test_mycoordinator cannot be resolved. Am I going about testing this class the wrong way? Is there a pythonic way I should be testing it?
Here is what my test class looks like so far:
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)
And here is the class I am trying to test:
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
0, we can now mock Java constructors with Mockito. This allows us to return a mock from every object construction for testing purposes. Similar to mocking static method calls with Mockito, we can define the scope of when to return a mock from a Java constructor for a particular Java class.
Just assign the exception to side_effect instead: mockedObj. raiseError. side_effect = Exception("Test") . You don't have to no: and his edit has a third way of doing it where he is making a Mock with the side effect set, but its still a valid, and good thing to know how to do in testing.
There is no need to provide a separate constructor. Mocking patches your code to replace objects with mocks. Just use the mock.patch()
decorator on your test methods; it'll pass in references to the generated mock objects.
Both producer.Producer()
and consumer.Consumer()
are then mocked out before you create the instance:
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)])
So the moment test_send_data
is called, the mock.patch()
code replaces the producer.Producer
reference with a mock object. Your MyCoordinator
class then uses those mock objects rather than the real code. calling producer.Producer()
returns a new mock object (the same object that mock_producer.return_value
references), etc.
I've made the assumption that producer
and consumer
are top-level module names. If they are not, provide the full import path. From the mock.patch()
documentation:
target should be a string in the form
'package.module.ClassName'
. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are callingpatch()
from. The target is imported when the decorated function is executed, not at decoration time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With