简体   繁体   English

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

[英]How do I mock part of a python constructor just for testing?

I am new to Python, so I apologize if this is a duplicate or overly simple question. 我是Python的新手,所以如果这是一个重复或过于简单的问题我会道歉。 I have written a coordinator class that calls two other classes that use the kafka-python library to send/read data from Kafka. 我编写了一个协调器类,调用另外两个使用kafka-python库来发送/读取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. 我希望我可以创建一个替代构造函数,我可以将我的模拟对象传递给,但这似乎不起作用,因为我得到一个错误,test_mycoordinator无法解决。 Am I going about testing this class the wrong way? 我是否会以错误的方式测试这门课程? Is there a pythonic way I should be testing it? 我应该测试一种pythonic方式吗?

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

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; 只需在测试方法上使用mock.patch()装饰器 ; 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: 创建实例之前 ,然后模拟出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)])

So the moment test_send_data is called, the mock.patch() code replaces the producer.Producer reference with a mock object. 所以当test_send_datamock.patch()代码用mock对象替换producer.Producer引用。 Your MyCoordinator class then uses those mock objects rather than the real code. 然后,您的MyCoordinator类使用这些模拟对象而不是真实代码。 calling producer.Producer() returns a new mock object (the same object that mock_producer.return_value references), etc. 调用producer.Producer()返回一个新的模拟对象(与mock_producer.return_value引用的对象相同)等。

I've made the assumption that producer and consumer are top-level module names. 我假设producerconsumer是顶级模块名称。 If they are not, provide the full import path. 如果不是,请提供完整的导入路径。 From the mock.patch() documentation: mock.patch()文档:

target should be a string in the form 'package.module.ClassName' . target应该是'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 calling patch() from. 导入目标并将指定的对象替换为新对象,因此目标必须可从您调用patch()的环境中导入。 The target is imported when the decorated function is executed, not at decoration time. 执行修饰函数时导入目标,而不是在装饰时导入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM