使用mock patch模拟实例方法

117 投票
3 回答
157530 浏览
提问于 2025-04-17 08:11

我在测试一个Django应用的时候,想用一个叫做Mock测试库的东西来模拟一些情况。但是我好像没搞明白,想做的事情总是无法实现,具体是这样的:

models.py
from somelib import FooClass
    
class Promotion(models.Model):
    foo = models.ForeignKey(FooClass)
    def bar(self):
        print "Do something I don't want!"
test.py
class ViewsDoSomething(TestCase):
    view = 'my_app.views.do_something'
    
    def test_enter_promotion(self):
        @patch.object(my_app.models.FooClass, 'bar')
        def fake_bar(self, mock_my_method):
            print "Do something I want!"
            return True
    
        self.client.get(reverse(view))

我哪里做错了呢?

3 个回答

7

如果你想对被模拟的方法使用 assert_called 等功能,可以使用 patch.object,并把替代的方法放在 MagicMock(side_effect=) 中,比如:

with patch.object(class_to_mock, attribute_name, \
 MagicMock(side_effect=replacement_method)) as replacement_method_mock:

例如:

from unittest.mock import patch, MagicMock

def fake_bar(self):
    print "Do something I want!"
    return True

def test_enter_promotion(self):
    with patch.object(my_app.models.FooClass, 'bar', MagicMock(side_effect=fake_bar)) as fake_bar_mock:
        self.client.get(reverse(view))
        # Do something I want!
        fake_bar_mock.assert_called()
45

哦,我之前搞不清楚这个补丁装饰器应该放在哪里。现在已经修好了:

class ViewsDoSomething(TestCase):
    view = 'my_app.views.do_something'

    @patch.object(my_app.models.FooClass, 'bar')
    def test_enter_promotion(self, mock_method):
        self.client.get(reverse(view))
103

在Kit的回答基础上,添加第三个参数给patch.object()可以指定你想要模拟的对象或方法。否则,它会使用一个默认的MagicMock对象。

    def fake_bar(self):
        print "Do something I want!"
        return True

    @patch.object(my_app.models.FooClass, 'bar', fake_bar)
    def test_enter_promotion(self):
        self.client.get(reverse(view))
        # Do something I want!

需要注意的是,如果你把模拟的对象作为第三个参数指定,那么默认的MagicMock()不再被传入到被修改的对象中了——也就是说,不再是:

def test_enter_promotion(self, mock_method):

而是:

def test_enter_promotion(self):

https://docs.python.org/3/library/unittest.mock.html#patch-object

撰写回答