【问题标题】:mocking a function within a class method在类方法中模拟函数
【发布时间】:2015-06-10 02:04:36
【问题描述】:

我想模拟一个在 Django 项目中测试类方法时在类方法中调用的函数。考虑以下结构:

app/utils.py

def func():
    ...
    return resp  # outcome is a HTTPResponse object

app/models.py

from app.utils import func

class MyModel(models.Model):
    
    # fields
    
    def call_func(self):
        ...
        func()
        ...

app/tests/test_my_model.py

from django.test import TestCase
import mock    

from app.models import MyModel

class MyModelTestCase(TestCase):

    fixtures = ['my_model_fixtures.json']

    def setUp(self):
        my_model = MyModel.objects.get(id=1)

    @mock.patch('app.utils.func')
    def fake_func(self):
        return mock.MagicMock(headers={'content-type': 'text/html'},
                              status_code=2000, 
                              content="Fake 200 Response"))

    def test_my_model(self):
        my_model.call_func()
        ...  # and asserting the parameters returned by func

当我运行测试时,模拟函数fake_func() 被避免,而是调用真正的func()。我猜mock.patch 装饰器中的范围可能是错误的,但我找不到让它工作的方法。我该怎么办?

【问题讨论】:

    标签: python django unit-testing mocking


    【解决方案1】:

    您的代码存在三个问题:

    1) 正如 Daniel Roseman 所说,您需要修补调用函数的模块,而不是定义函数的位置

    2) 此外,您需要修饰将实际执行调用模拟函数的代码的测试方法。

    3) 最后,您还需要将模拟版本作为参数传递给您的测试方法,可能是这样的:

    fake_response = mock.MagicMock(headers={'content-type': 'text/html'},
                              status_code=2000, 
                              content="Fake 200 Response"))
    
    
    class MyModelTestCase(TestCase):
    
        fixtures = ['my_model_fixtures.json']
    
        def setUp(self):
            my_model = MyModel.objects.get(id=1)
    
        @mock.patch('app.models.func', return_value=fake_response)
        def test_my_model(self, fake_response):  # the mock goes in as a param or else you get number of arguments error!
            my_model.call_func()
            self.assertTrue(fake_response.called)
    

    【讨论】:

    • 如果我需要真正的模拟函数的副作用,而不仅仅是返回值怎么办?例如,在这种情况下,考虑在模拟函数时我需要打印一些东西。
    • 在这个问题的真实上下文中,我不需要fake_response 本身,我将在call_func 中解析它并更新MyModel 中的一个字段。
    • 我又复习了一遍,你是对的,我什至可以使用fake_func() 代替fake_response 作为return_value。谢谢!
    【解决方案2】:

    正如docs 解释的那样,您需要在调用它的地方模拟 func,而不是在它定义的地方。所以:

    @mock.patch('app.models.func')
    

    【讨论】:

    • 这也不起作用,仍然调用了真正的函数。我使用@mock.patch 对吗?在正确的地方?
    • 啊,不。你应该装饰你的测试方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 2013-11-28
    • 2021-04-02
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多