如何修补python方法以运行测试用例?

时间:2018-06-08 19:08:21

标签: python django unit-testing mocking

我正在研究的一个项目基本上都是使用django。在编写模型时,我发现有必要覆盖save()方法来分离要由worker工作的任务:

class MyModel(models.Model)
    def _start_processing(self):
        my_task.apply_async(args=['arg1', ..., 'argn'])
    def save(self, *args, **kwargs):
        """Saves the model object to the database"""
        # do some stuff
        self._start_processing()
        # do some more stuff
        super(MyModel, self).save(*args, **kwargs)

在我的测试人员中,我想测试# do some stuff# do some more stuff指定的保存覆盖部分,但不想运行任务。要做到这一点,我相信我应该使用模拟(我很新)。

在我的测试类中,我将其设置为跳过任务调用:

class MyModelTests(TestCase):
    def setUp(self):
        # Mock the _start_processing() method. Ha!
        @patch('my_app.models.MyModel._start_processing')
        def start_processing(self, mock_start_processing):
            print('This is when the task would normally be run, but this is a test!')

        # Create a model to test with
        self.test_object = MyModelFactory()

由于工厂创建并保存了模型的实例,因此我需要在调用之前覆盖_start_processing()方法。以上似乎不起作用(任务运行并失败)。我错过了什么?

1 个答案:

答案 0 :(得分:1)

首先,你必须包装到装饰器而不是你想要用作替换的函数,但是"范围"你的模拟应该工作。因此,例如,如果您需要为整个_start_processing类模拟MyModelTests,则应将装饰器放在类定义之前。如果仅用于一种测试方法 - 仅用它包装测试方法。

其次,在类之外的某处定义start_processing函数,并传递@patch('my_app.models.MyModel._start_processing', new=start_processing),这样它就会知道用什么作为实际方法的替代。但要注意匹配实际的方法签名,所以只需使用

def start_processing(self):
    print('This is when the task would normally be run, but this is a test!')

第三,你必须为这个类中的每个测试用例添加mock_start_processing参数(test _... methods),因为mocking的工作原理如下:)。

最后。您必须了解正在修补的target。您当前的my_app.models.MyModel._start_processing可能会被破坏。您必须使用其使用的路径来修补类,而不是使用DEFINED。因此,如果您要在MyModelFactory内创建TestCase的对象,并且MyModelFactory位于my_app.factories,并将MyModel导入from .models import MyModel,您将必须使用@patch('my_app.factories.MyModel._start_processing'),而不是'my_app.models.MyModel._start_processing'

希望它有所帮助。