Cpp*_*ner 22 python unit-testing mocking
class HelloWorld(object):
def say_it(self):
return 'Hello I am Hello World'
def i_call_hello_world(hw_obj):
print 'here... check type: %s' %type(HelloWorld)
if isinstance(hw_obj, HelloWorld):
print hw_obj.say_it()
from mock import patch, MagicMock
import unittest
class TestInstance(unittest.TestCase):
@patch('__main__.HelloWorld', spec=HelloWorld)
def test_mock(self,MK):
print type(MK)
MK.say_it.return_value = 'I am fake'
v = i_call_hello_world(MK)
print v
if __name__ == '__main__':
c = HelloWorld()
i_call_hello_world(c)
print isinstance(c, HelloWorld)
unittest.main()
Run Code Online (Sandbox Code Playgroud)
这是追溯
here... check type: <type 'type'>
Hello I am Hello World
True
<class 'mock.MagicMock'>
here... check type: <class 'mock.MagicMock'>
E
======================================================================
ERROR: test_mock (__main__.TestInstance)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1224, in patched
return func(*args, **keywargs)
File "t.py", line 18, in test_mock
v = i_call_hello_world(MK)
File "t.py", line 7, in i_call_hello_world
if isinstance(hw_obj, HelloWorld):
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
----------------------------------------------------------------------
Ran 1 test in 0.002s
Run Code Online (Sandbox Code Playgroud)
Q1.为什么抛出这个错误?他们是<class type='MagicMock>
Q2.如果错误得到修复,如何暂停模拟,以便第一行通过?
来自doc
通常,对象的class属性将返回其类型.对于具有spec 类的模拟对象,请返回spec类.这允许模拟对象为它们替换/伪装的对象传递isinstance测试:
mock = Mock(spec=3)
isinstance(mock, int)
True
Run Code Online (Sandbox Code Playgroud)
谢谢
Mic*_*ico 41
恕我直言,这是一个很好的问题,并说" 不使用isinstance
,改用鸭子打字 "是一个糟糕的答案.鸭子打字很棒,但不是银弹.有时isinstance
是必要的,即使它不是pythonic.例如,如果您使用的是某些不是pythonic的库或遗留代码,则必须使用isinstance
.它只是现实世界,而mock是为了适应这种工作而设计的.
在代码中,最大的错误是你写的时候:
@patch('__main__.HelloWorld', spec=HelloWorld)
def test_mock(self,MK):
Run Code Online (Sandbox Code Playgroud)
从我们阅读的patch
文档(强调是我的):
在函数体或with语句的主体内部,使用新对象修补目标.
这意味着当您修补HelloWorld
类对象时,引用HelloWorld
将被函数MagicMock
上下文的对象替换test_mock()
.
然后,i_call_hello_world()
执行时if isinstance(hw_obj, HelloWorld):
HelloWorld
是一个MagicMock()
对象而不是一个类(如错误所示).
这种行为是因为作为修补类引用的副作用,第二个参数isinstance(hw_obj, HelloWorld)
成为一个对象(一个MagicMock
实例).这不是一个class
或一个type
.理解此行为的简单实验是修改i_call_hello_world()
如下:
HelloWorld_cache = HelloWorld
def i_call_hello_world(hw_obj):
print 'here... check type: %s' %type(HelloWorld_cache)
if isinstance(hw_obj, HelloWorld_cache):
print hw_obj.say_it()
Run Code Online (Sandbox Code Playgroud)
错误将消失,因为加载模块时HelloWorld
会保存对类的原始引用HelloWorld_cache
.当应用补丁时,它会改变HelloWorld
而不是HelloWorld_cache
.
不幸的是,之前的实验并没有给我们任何方式来处理像你这样的案例,因为你不能改变库或遗留代码来引入像这样的技巧.而且,这些是我们希望在代码中看不到的那种技巧.
好消息是,你可以做一些事情,但你不能随便patch
的HelloWord
,你必须在模块中引用 isinstace(o,HelloWord)
的代码进行测试.最好的方法取决于你必须解决的真实案例.在您的示例中,您可以创建一个Mock
用作HelloWorld
对象,使用spec
参数将其打扮为HelloWorld
实例并传递isinstance
测试.这正是spec
设计目标之一.您的测试将如下所示:
def test_mock(self):
MK = MagicMock(spec=HelloWorld) #The hw_obj passed to i_call_hello_world
print type(MK)
MK.say_it.return_value = 'I am fake'
v = i_call_hello_world(MK)
print v
Run Code Online (Sandbox Code Playgroud)
而单位测试部分的输出是
<class 'mock.MagicMock'>
here... check type: <type 'type'>
I am fake
None
Run Code Online (Sandbox Code Playgroud)
Michele d'Amico 在我看来提供了正确答案,我强烈建议您阅读.但是我花了一段时间才知道,并且,我相信我将来会回到这个问题,我认为一个最小的代码示例将有助于澄清解决方案并提供快速参考:
from mock import patch, mock
class Foo(object): pass
# Cache the Foo class so it will be available for isinstance assert.
FooCache = Foo
with patch('__main__.Foo', spec=Foo):
foo = Foo()
assert isinstance(foo, FooCache)
assert isinstance(foo, mock.mock.NonCallableMagicMock)
# This will cause error from question:
# TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
assert isinstance(foo, Foo)
Run Code Online (Sandbox Code Playgroud)
Ned*_*der -7
不要使用isinstance
,而是检查该say_it
方法是否存在。如果该方法存在,则调用它:
if hasattr(hw_obj, 'say_it'):
print hw_obj.say_it()
Run Code Online (Sandbox Code Playgroud)
无论如何,这是一个更好的设计:依赖类型信息要脆弱得多。
归档时间: |
|
查看次数: |
12736 次 |
最近记录: |