How can I mock patch a class used in an isinstance test?
我想测试函数
1 2 3 4 | def is_myclass(obj): """This absurd stub is a simplified version of the production code.""" isinstance(obj, MyClass) MyClass() |
文档
unittest.mock的python文档说明了解决EDOCX1问题的三种方法:
- 百万千克1将
型
__class__ Normally the
__class__ attribute of an object will return its type. For a mock object with a spec,__class__ returns the spec class instead. This allows mock objects to pass isinstance() tests for the object they are replacing / masquerading as:
1
2
3 >>> mock = Mock(spec=3)
>>> isinstance(mock, int)
True
__class__ is assignable to, this allows a mock to pass anisinstance() check without forcing you to use a spec:
1
2
3
4 >>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True[...]
If you use
spec orspec_set andpatch() is replacing a class, then the return value of the created mock will have the same spec.
1
2
3
4
5
6 >>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
号
测验
我已经编写了五个测试,每个测试首先尝试复制三个解决方案中的每一个,然后对目标代码进行实际的测试。典型的模式是
所有测试都失败。
测试1
这是文档中提供的用于
这相当于米歇尔达米科对类似问题的回答是站在立场和嘲弄。
测试2
这是测试1的修补等效物。
测试3
这是文档中提供的用于
测试4
这是测试3的修补等效物。对
测试5
这是调用修补程序时使用
代码
这段代码是作为一个独立的测试模块编写的,打算在Pycharm IDE中运行。您可能需要修改它以在其他测试环境中运行。
模块temp2.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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 |
号
你不能模仿EDOCX1的第二个参数(0),不。当第一个参数通过测试时,你发现的文档担心会进行模仿。如果您想要生成一个可接受的东西作为
您可以使用一个子类来代替
1 2 3 | class MockedSubClass(MyClass): def __new__(cls, *args, **kwargs): return mock.Mock(spec=cls) # produce a mocked instance when called |
把它补上:
1 | mock.patch('temp2.MyClass', new=MockedSubClass) |
并使用该类的一个实例作为模拟:
1 | instance = mock.Mock(spec=MockedSubClass) |
或者,这就简单多了,只使用
1 2 | with mock.patch('temp2.MyClass', new=mock.Mock) as mocked_class: is_myclass(mocked_class()) |
不管怎样,您的测试都会通过:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> with mock.patch('temp2.MyClass', new=MockedSubClass) as mocked_class: ... instance = mock.Mock(spec=MockedSubClass) ... assert isinstance(instance, mocked_class) ... is_myclass(instance) ... >>> # no exceptions raised! ... >>> with mock.patch('temp2.MyClass', new=mock.Mock) as mocked_class: ... is_myclass(mocked_class()) ... >>> # no exceptions raised! ... |
对于您的特定测试,以下是失败的原因: