python 单元测试模拟无法处理部分限定名称
python unittest mock can't handle partially qualified name
如果我有两个包含以下内容的文件:
test_helper.py
:
class Installer:
def __init__(self, foo, bar, version):
# Init stuff
raise Exception('If we're here, mock didn't work')
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
raise Exception('If we're here, mock didn't work')
并且
test.py
:
import unittest
from mock import patch
from test_helper import Installer
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
@patch('tests.test_helper.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
上面的代码没有正确测试。模拟应用不正确:
File "/Library/Python/2.7/site-packages/mock-1.3.0-py2.7.egg/mock/mock.py", line 947, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'Installer' to be called once. Called 0 times.
如果我在 test.py
中进行以下更改:
- 将
from test_helper import Installer
更改为 import test_helper
,并且
- 将
with Installer('foo', 'bar', 1) as installer:
更改为with test_helper.Installer('foo', 'bar', 1) as installer:
然后代码就可以工作了。为什么模拟仅在我使用完全限定名称时适用?它应该在部分合格的情况下工作吗?
您正在 test.py
中测试您的 Deployer
class,它正在调用 Installer
。这个安装程序就是你想要模拟的。所以,你的装饰器应该考虑到这一点。
我不知道你具体是从哪里测试的。但举个例子,如果你 运行 你的测试来自与 test.py
相同的级别,那么你可以简单地对你的装饰器执行此操作,它应该可以工作:
import unittest
from dower import Installer
from mock import patch
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
@patch('test.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
if __name__ == '__main__':
unittest.main()
注意:您不应在安装程序模块中进行模拟。在这种情况下,您 不关心 安装程序。只是它 returns 你的 Mock
,所以你可以继续测试你的 Deployer
的行为。以这种方式思考,你就会明白为什么你必须模拟你正在测试的东西。
如果我有两个包含以下内容的文件:
test_helper.py
:
class Installer:
def __init__(self, foo, bar, version):
# Init stuff
raise Exception('If we're here, mock didn't work')
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
raise Exception('If we're here, mock didn't work')
并且
test.py
:
import unittest
from mock import patch
from test_helper import Installer
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
@patch('tests.test_helper.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
上面的代码没有正确测试。模拟应用不正确:
File "/Library/Python/2.7/site-packages/mock-1.3.0-py2.7.egg/mock/mock.py", line 947, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'Installer' to be called once. Called 0 times.
如果我在 test.py
中进行以下更改:
- 将
from test_helper import Installer
更改为import test_helper
,并且 - 将
with Installer('foo', 'bar', 1) as installer:
更改为with test_helper.Installer('foo', 'bar', 1) as installer:
然后代码就可以工作了。为什么模拟仅在我使用完全限定名称时适用?它应该在部分合格的情况下工作吗?
您正在 test.py
中测试您的 Deployer
class,它正在调用 Installer
。这个安装程序就是你想要模拟的。所以,你的装饰器应该考虑到这一点。
我不知道你具体是从哪里测试的。但举个例子,如果你 运行 你的测试来自与 test.py
相同的级别,那么你可以简单地对你的装饰器执行此操作,它应该可以工作:
import unittest
from dower import Installer
from mock import patch
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
@patch('test.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
if __name__ == '__main__':
unittest.main()
注意:您不应在安装程序模块中进行模拟。在这种情况下,您 不关心 安装程序。只是它 returns 你的 Mock
,所以你可以继续测试你的 Deployer
的行为。以这种方式思考,你就会明白为什么你必须模拟你正在测试的东西。