在python中模拟模块中的所有类

2024-05-19 14:14:01 发布

您现在位置:Python中文网/ 问答频道 /正文

在我的测试环境中,我在3个不同的目录中有一个test_a.py、a.py和b.py。在

b.py公司

class SBD():

def __init__(self):
    print("SBD created (In B)")

a.py

^{pr2}$

测试

import unittest
import sys
from unittest.mock import Mock,patch,MagicMock

sys.path.append("../abc/")
import b as c
sys.modules['b'] = MagicMock(spec=c)

sys.path.append("../xyz/")
import a

class TestStringMethods(unittest.TestCase):

    def test_isupper(self):
        a.fun1()


if __name__ == '__main__':
    unittest.main()

我想在同一个模块中模仿它们,所以我想用同一个模块来嘲笑它们。但是当我运行test_a.py时,它会给我一个错误,说“SBD”没有定义。我做错什么了?在


Tags: 模块pathpytestimportself测试环境main
1条回答
网友
1楼 · 发布于 2024-05-19 14:14:01

MagicMock实例与模块向导入机制提供的信息不同。即使使用spec,也没有在mock上定义实际的SDB属性,因此from b import *找不到它。在

进口机器尝试两种不同的方法:

  • 它试图访问名称__all__;如果定义了它,它必须是要导入的名称字符串列表。在
  • 如果__all__未定义,则取__dict__属性的键,过滤掉以下划线开头的名称。在

因为您的b模块没有定义__all__列表,所以取而代之的是__dict__键。对于针对模块指定的MagicMock实例,__dict__属性只由带有_下划线的名称和mock_calls属性组成。from b import *仅进口mock_calls

>>> import a as c
>>> module_mock = MagicMock(spec=c)
>>> [n for n in module_mock.__dict__ if n[:1] != '_']
['method_calls']

我强烈建议不要嘲笑整个模块;这样做将要求您推迟导入a,而且很脆弱。修补程序是永久的(测试结束时不会自动撤消),并且不支持重复运行测试或以随机顺序运行测试。在

但是,如果来实现这一点,您可以首先向mock添加一个__all__属性:

^{pr2}$

就个人而言,我应该避免使用from module import *语法。如果我无论如何都不能阻止它被使用,下一步就是在导入之后对a应用补丁,在b模块上循环以获得指定的替换:

# avoid manipulating sys.path if at all possible. Move that to a PYTHONPATH
# variable or install the modules properly.
import unittest
from unittest import mock

import a
import b

class TestStringMethods(unittest.TestCase):
    def setUp(self):
        # mock everything `from b import *` would import
        b_names = getattr(b, '__all__', None)
        if b_names is None:
            b_names = [n for n in b.__dict__ if n[:1] != '_']
        self.b_mocks = {}
        for name in b_names:
            orig = getattr(b, name, None)
            if orig is None:
                continue
            self.b_mocks[name] = mock.patch.object(a, name, spec=orig)
            self.b_mocks[name].start()
            self.addCleanup(self.b_mocks[name].stop)

    def test_isupper(self):
        a.fun1()

这使sys.modules['b']保持不变,并处理from *将加载的相同名称。测试结束后,补片再次被移除。在

以上测试输出:

$ python test_a.py
In module A
SBD created(In A)
.
                                   
Ran 1 test in 0.001s

OK

相关问题 更多 >