Python 模拟补丁不模拟对象

问题描述 投票:0回答:2

我正在使用 python 模拟库,我不确定为什么会得到这个结果。为什么只有第二个被嘲笑而不是第一个?最好的方法是什么?

import unittest
from unittest.mock import patch, Mock

import requests
from requests import Session


def my_func():
    s = Session()
    print('my_func', type(s))


def my_func2():
    s = requests.Session()
    print('my_func2', type(s))


class Test(unittest.TestCase):

    @patch("requests.Session", new=Mock)
    def test1(self, *args):
        my_func()

    @patch("requests.Session", new=Mock)
    def test2(self, *args):
        my_func2()

输出

my_func <class 'requests.sessions.Session'>
my_func2 <class 'unittest.mock.Mock'>
python mocking python-unittest python-unittest.mock
2个回答
4
投票

这是 Python 命名空间的一个棘手的细微差别。 在第一个

my_func()
中,您仅使用了
Session
(使用
from requests import Session
将其导入到模块命名空间后)。 在
my_func2()
中,您使用了
requests.Session

@patch('requests.Session')
正在替换
Session
模块中的 requests
,因此 
requests.Session
 将成为模拟对象。  但它并不能取代每个模块中对 
Session
 的所有引用——事实上,这在 Python 中很难做到。

实际上,在基本层面上,

patch()

所做的只是设置
requests.Session = Mock()
。  但要替换对模块全局命名空间中已有的 
Session
 的引用,还必须设置 
globals()['Session'] = Mock()

换句话说,您必须在正确的命名空间中修补对象。

如果您有某个模块

foo.py

包含:

from requests import Session def my_func(): s = Session() ...
然后是一个单独的测试模块

test_foo.py

,您想在其中模拟
Session
类,您必须在
foo
的命名空间中对其进行修补,例如:

import foo @patch('foo.Session', ...) def test_my_func(): ...
    

0
投票
来自文档:

在哪里打补丁

patch() 的工作原理是(暂时)将一个名称指向的对象更改为另一个名称所指向的对象。可以有许多名称指向任何单个对象,因此为了使修补工作正常进行,您必须确保修补被测试系统使用的名称。

基本原则是在查找对象的位置进行修补,该位置不一定与定义对象的位置相同。几个示例将有助于阐明这一点。

假设我们有一个要测试的项目,其结构如下:

a.py -> Defines SomeClass b.py -> from a import SomeClass -> some_function instantiates SomeClass
现在我们想要测试 some_function 但我们想使用 patch() 模拟 SomeClass。问题是,当我们导入模块 b 时(我们必须这样做),它会从模块 a 导入 SomeClass。如果我们使用 patch() 来模拟 a.SomeClass 那么它不会对我们的测试产生影响;模块 b 已经引用了真正的 SomeClass,看起来我们的修补没有效果。

关键是在使用 SomeClass 的地方(或者查找它的地方)修补它。在这种情况下,some_function 实际上会在模块 b 中查找 SomeClass,我们已将其导入到该模块中。修补程序应如下所示:

@patch('b.SomeClass')
但是,请考虑另一种情况,其中不是从 import SomeClass 模块 b 而是导入 a 并且 some_function 使用 a.SomeClass。这两种导入形式都很常见。在这种情况下,我们想要修补的类正在模块中查找,因此我们必须修补 a.SomeClass :

@patch('a.SomeClass')
    
© www.soinside.com 2019 - 2024. All rights reserved.