我如何模拟部分python构造函数进行测试?

jen*_*ton 15 python unit-testing mocking python-mock

我是Python的新手,所以如果这是一个重复或过于简单的问题我会道歉.我编写了一个协调器类,调用另外两个使用kafka-python库来发送/读取Kafka数据的类.我想为我的协调员课程编写一个单元测试,但是我很难弄清楚如何最好地解决这个问题.我希望我可以创建一个替代构造函数,我可以将我的模拟对象传递给,但这似乎不起作用,因为我得到一个错误,test_mycoordinator无法解决.我是否会以错误的方式测试这门课程?我应该测试一种pythonic方式吗?

这是我的测试类到目前为止的样子:

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)
Run Code Online (Sandbox Code Playgroud)

以下是我要测试的课程:

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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 23

不需要提供单独的构造函数.模拟修补代码以用模拟替换对象.只需在测试方法上使用mock.patch()装饰器 ; 它将传递对生成的模拟对象的引用.

双方producer.Producer()consumer.Consumer()随后被嘲笑了之前创建的实例:

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)])
Run Code Online (Sandbox Code Playgroud)

因此,test_send_data调用该时刻,mock.patch()代码将producer.Producer模拟对象替换为引用.MyCoordinator然后,您的类使用那些模拟对象而不是真实代码.调用producer.Producer()返回一个新的模拟对象(mock_producer.return_value引用的同一个对象),等等.

我已经假设producer并且consumer是顶级模块名称.如果不是,请提供完整的导入路径.从mock.patch()文档:

target应该是表单中的字符串'package.module.ClassName'.导入目标并使用新对象替换指定的对象,因此目标必须可从您调用的环境导入patch().执行修饰函数时导入目标,而不是在装饰时导入.