【发布时间】:2018-09-17 23:53:11
【问题描述】:
我想测试函数is_myclass。请帮助我了解如何编写成功的测试。
def is_myclass(obj):
"""This absurd stub is a simplified version of the production code."""
isinstance(obj, MyClass)
MyClass()
文档
unittest.mock 的 Python 文档说明了解决 isinstance 问题的三种方法:
- 将
spec参数设置为真实类。 - 将真实类分配给
__class__属性。 - 在真实类的补丁中使用
spec。
__class__通常,对象的
__class__属性将返回其类型。对于具有规范的模拟对象,__class__返回规范类。这允许模拟对象通过 isinstance() 测试它们正在替换/伪装为的对象:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__可分配给,这允许模拟通过isinstance()检查而不强制您使用规范:>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True[...]
如果您使用
spec或spec_set并且patch()正在替换一个类,那么创建的模拟的返回值将具有相同的规范。>>> Original = Class >>> patcher = patch('__main__.Class', spec=True) >>> MockClass = patcher.start() >>> instance = MockClass() >>> assert isinstance(instance, Original) >>> patcher.stop()
测试
我编写了五个测试,每个测试首先尝试重现三个解决方案中的每一个,然后对目标代码进行实际测试。典型的模式是assert isinstance,然后调用is_myclass。
所有测试都失败了。
测试 1
这是文档中为使用spec 提供的示例的完整副本。它
通过使用 spec=<class> 而不是 spec=<instance> 与文档不同。它通过
本地断言测试,但对 is_myclass 的调用失败,因为 MyClass 未被模拟。
这相当于 Michele d'Amico 在isinstance and Mocking 中对类似问题的回答。
测试 2
这是测试 1 的修补等效项。spec 参数无法设置模拟 MyClass 的 __class__,并且测试在本地 assert isinstance 失败。
测试 3
这是文档中为使用__class__ 提供的示例的完整副本。它通过
本地断言测试,但对 is_myclass 的调用失败,因为 MyClass 未被模拟。
测试 4
这是测试 3 的修补等效项。对 __class__ 的分配确实设置了模拟 MyClass 的 __class__,但这不会改变其类型,因此测试无法通过本地 assert isinstance。
测试 5
这是在补丁调用中使用spec 的近似副本。它通过了本地断言测试,但仅通过访问 MyClass 的本地副本。由于is_myclass 中未使用此局部变量,因此调用失败。
代码
此代码是作为一个独立的测试模块编写的,旨在在 PyCharm IDE 中运行。您可能需要对其进行修改才能在其他测试环境中运行。
模块 temp2.py
import unittest
import unittest.mock as mock
class WrongCodeTested(Exception):
pass
class MyClass:
def __init__(self):
"""This is a simplified version of a production class which must be mocked for unittesting."""
raise WrongCodeTested('Testing code in MyClass.__init__')
def is_myclass(obj):
"""This absurd stub is a simplified version of the production code."""
isinstance(obj, MyClass)
MyClass()
class ExamplesFromDocs(unittest.TestCase):
def test_1_spec(self):
obj = mock.Mock(spec=MyClass)
print(type(MyClass)) # <class 'type'>
assert isinstance(obj, MyClass) # Local assert test passes
is_myclass(obj) # Fail: MyClass instantiated
def test_2_spec_patch(self):
with mock.patch('temp2.MyClass', spec=True) as mock_myclass:
obj = mock_myclass()
print(type(mock_myclass)) # <class 'unittest.mock.MagicMock'>
print(type(MyClass)) # <class 'unittest.mock.MagicMock'>
assert isinstance(obj, MyClass) # Local assert test fails
def test_3__class__(self):
obj = mock.Mock()
obj.__class__ = MyClass
print(type(MyClass)) # <class 'type'>
isinstance(obj, MyClass) # Local assert test passes
is_myclass(obj) # Fail: MyClass instantiated
def test_4__class__patch(self):
Original = MyClass
with mock.patch('temp2.MyClass') as mock_myclass:
mock_myclass.__class__ = Original
obj = mock_myclass()
obj.__class__ = Original
print(MyClass.__class__) # <class 'temp2.MyClass'>
print(type(MyClass)) # <class 'unittest.mock.MagicMock'>
assert isinstance(obj, MyClass) # Local assert test fails
def test_5_patch_with_spec(self):
Original = MyClass
p = mock.patch('temp2.MyClass', spec=True)
MockMyClass = p.start()
obj = MockMyClass()
print(type(Original)) # <class 'type'>
print(type(MyClass)) # <class 'unittest.mock.MagicMock'>
print(type(MockMyClass)) # <class 'unittest.mock.MagicMock'>
assert isinstance(obj, Original) # Local assert test passes
is_myclass(obj) # Fail: Bad type for MyClass
【问题讨论】:
标签: python unit-testing mocking python-unittest