考虑以下代码(不是一个非常好的设计,但重点):
class A(object):
def __init__(self,filepath):
self._access_file_system(filepath)
def get(self):
return self._result_dict
class B(object):
def __init__(self,filepath1,filepath2):
self._filepath1 = filepath1
self._filepath2 = filepath2
def foo(self):
a1 = A(self._filepath1).get()
a2 = A(self._filepath2).get()
return a1['result']==a2['result']
现在,如果我想测试B.foo()
,我需要模拟A
(因为它访问构造函数中的文件系统)。
要编写一个测试,以确保在B.foo()
和False
提供不同值时a1.get()
返回a2.get()
,我还需要模拟B.get()
。
因此,测试函数应该如下所示:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
b = B('/file1','/file2')
res = b.foo()
assert res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
#Doesn't work -
#the assignment doesn't propagate into the objects instantiated inside foo()
#A.get = mock_get
#The assigned method propagates into the class definition,
#so it works - BUT WHY?!
a = A(None)
a.get = mock_get
b = B('/file1', '/file2')
res = b.foo()
assert not res
现在,奇怪的一点 - 从代码中的注释可以看出,如果我们将mock_get
分配给类,它就不会传播,但是如果我们创建一个实例并分配给它,它传播到该类的其他实例。
我认为这种行为与mock
的内部机制有关,因此对我来说理解它很重要,要正确使用这个库及其丰富的功能。< / p>
那么,有没有人有线索?
答案 0 :(得分:1)
在第一种情况下,我无法看到您正在修补get
方法的任何地方。在调用get
之前,您应该将模拟值分配给A
B
方法。例如,为什么以下测试失败?:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
MockA.get = mock_get
b = B('/file1','/file2')
res = b.foo()
assert not res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
之前行为的原因是我们忘记修补对象(A)的返回值,在本例中为MockA
,而不是对象本身(MockA
)。 A
对象是实例化A
类的结果,您应该访问return_value
类A
的方法。在您的示例中,它将类似于此:
import mock
mock_get = mock.MagicMock(side_effect=[{'result': 0}, {'result': 1}])
@mock.patch('__main__.A')
def test_foo(MockA):
MockA.return_value.get = mock_get
b = B('/file1','/file2')
res = b.foo()
assert res
MockA.assert_any_call('/file1')
MockA.assert_any_call('/file2')
您可以查看以下一些帖子,了解有关常见python单元测试陷阱的更多信息: