Python unittest 模拟配置没有扩散到测试方法
Python unittest mock configuration not proliferating to test method
我有一个复杂的 class,我想在测试中对其进行模拟。但是,我希望能够在我的模拟 class 上设置一个特定的属性,以便我的被测函数能够工作。下面是简单的、运行具有实际和预期输出的可用示例。
在我的示例中,为什么我的模拟配置没有扩散到 my_module.instantiate_class_do_stuff
?很明显 MyClass
确实被嘲笑了,但我试图配置 MyClass.a
的嘲笑只是没有坚持。
目录内容 tmp
:
tmp
├── __init__.py
├── my_module.py
└── test_my_module.py
请注意 __init__.py
为空。
文件内容 my_module.py
:
class MyClass:
def __init__(self):
# Do expensive operations that will be mocked in testing.
self.a = 7
def instantiate_class_do_stuff():
"""Create MyClass instance and do stuff with it"""
instance = MyClass()
print('Value of a in instantiate_class_call_method: {}'.format(instance.a))
# Do stuff with instance...
文件内容 test_my_module.py
:
import unittest
from unittest.mock import patch
from tmp import my_module
class MyTestCase(unittest.TestCase):
def setUp(self):
print('*' * 79)
def test_create_class_call_method_1(self):
"""Try using the Mock's configure_mock method."""
with patch('tmp.my_module.MyClass') as p:
p.configure_mock(a=10)
print('Value of a in test_create_class_call_method_1: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
def test_create_class_call_method_2(self):
"""Try passing in kwargs to patch, which should make it to
configure_mock, according to the docs.
"""
with patch('tmp.my_module.MyClass', a=10) as p:
print('Value of a in test_create_class_call_method_2: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
def test_create_class_call_method_alternate_2(self):
"""Try using patch.object instead of plain patch."""
with patch.object(my_module, 'MyClass', a=10) as p:
print('Value of a in test_create_class_call_method_3: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
来自 运行ning test_my_module.py
的实际输出:
*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598201104'>
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598270096'>
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598347088'>
运行ning test_my_module.py
的预期输出:
*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: 10
那么,当我的被测函数实际上是运行时,如何让我的属性配置生效?
嗯,这对我来说当然不是很直观,但我想通了。
我注意到在我的测试方法中,p
的值(MyClass
的模拟实例)有一个 __repr__
像 <MagicMock name='MyClass' id='140054079807440'>
.
然而,在被测函数内部,instantiate_class_do_stuff
,instance
的值有一个 __repr__
,如 <MagicMock name='MyClass()' id='140054079941392'>
。区别在于 MyClass
.
之后的 ()
所以,看来我没有模拟正确的东西 - 我想模拟 return 值 上的 a
属性MyClass
。
所以,这是一个有效的测试:
def test_creat_class_call_method(self):
# Create a mock which will be returned by MyClass.
m = MagicMock()
m.a = 10
with patch('tmp.my_module.MyClass', return_value=m) as p:
my_module.instantiate_class_do_stuff()
并且 my_module.instantiate_class_do_stuff
中的 print
语句打印以下内容:
Value of a in instantiate_class_do_stuff: 10
成功!
我有一个复杂的 class,我想在测试中对其进行模拟。但是,我希望能够在我的模拟 class 上设置一个特定的属性,以便我的被测函数能够工作。下面是简单的、运行具有实际和预期输出的可用示例。
在我的示例中,为什么我的模拟配置没有扩散到 my_module.instantiate_class_do_stuff
?很明显 MyClass
确实被嘲笑了,但我试图配置 MyClass.a
的嘲笑只是没有坚持。
目录内容 tmp
:
tmp
├── __init__.py
├── my_module.py
└── test_my_module.py
请注意 __init__.py
为空。
文件内容 my_module.py
:
class MyClass:
def __init__(self):
# Do expensive operations that will be mocked in testing.
self.a = 7
def instantiate_class_do_stuff():
"""Create MyClass instance and do stuff with it"""
instance = MyClass()
print('Value of a in instantiate_class_call_method: {}'.format(instance.a))
# Do stuff with instance...
文件内容 test_my_module.py
:
import unittest
from unittest.mock import patch
from tmp import my_module
class MyTestCase(unittest.TestCase):
def setUp(self):
print('*' * 79)
def test_create_class_call_method_1(self):
"""Try using the Mock's configure_mock method."""
with patch('tmp.my_module.MyClass') as p:
p.configure_mock(a=10)
print('Value of a in test_create_class_call_method_1: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
def test_create_class_call_method_2(self):
"""Try passing in kwargs to patch, which should make it to
configure_mock, according to the docs.
"""
with patch('tmp.my_module.MyClass', a=10) as p:
print('Value of a in test_create_class_call_method_2: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
def test_create_class_call_method_alternate_2(self):
"""Try using patch.object instead of plain patch."""
with patch.object(my_module, 'MyClass', a=10) as p:
print('Value of a in test_create_class_call_method_3: {}'
.format(p.a))
my_module.instantiate_class_do_stuff()
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
来自 运行ning test_my_module.py
的实际输出:
*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598201104'>
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598270096'>
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: <MagicMock name='MyClass().a' id='140029598347088'>
运行ning test_my_module.py
的预期输出:
*******************************************************************************
Value of a in test_create_class_call_method_1: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_2: 10
Value of a in instantiate_class_call_method: 10
*******************************************************************************
Value of a in test_create_class_call_method_3: 10
Value of a in instantiate_class_call_method: 10
那么,当我的被测函数实际上是运行时,如何让我的属性配置生效?
嗯,这对我来说当然不是很直观,但我想通了。
我注意到在我的测试方法中,p
的值(MyClass
的模拟实例)有一个 __repr__
像 <MagicMock name='MyClass' id='140054079807440'>
.
然而,在被测函数内部,instantiate_class_do_stuff
,instance
的值有一个 __repr__
,如 <MagicMock name='MyClass()' id='140054079941392'>
。区别在于 MyClass
.
()
所以,看来我没有模拟正确的东西 - 我想模拟 return 值 上的 a
属性MyClass
。
所以,这是一个有效的测试:
def test_creat_class_call_method(self):
# Create a mock which will be returned by MyClass.
m = MagicMock()
m.a = 10
with patch('tmp.my_module.MyClass', return_value=m) as p:
my_module.instantiate_class_do_stuff()
并且 my_module.instantiate_class_do_stuff
中的 print
语句打印以下内容:
Value of a in instantiate_class_do_stuff: 10
成功!