我遇到了一个问题,我认为这可能是我正在使用的库的一个错误。但是,我对python,unittest和unittest.mock库相当新,所以这可能只是我理解中的漏洞。
在为某些生产代码添加测试时,我遇到了错误,我生成了一个可以重现问题的最小样本:
import unittest
import mock
class noCtorArg:
def __init__(self):
pass
def okFunc(self):
raise NotImplemented
class withCtorArg:
def __init__(self,obj):
pass
def notOkFunc(self):
raise NotImplemented
def okWithArgFunc(self, anArgForMe):
raise NotImplemented
class BasicTestSuite(unittest.TestCase):
"""Basic test Cases."""
# passes
def test_noCtorArg_okFunc(self):
mockSUT = mock.MagicMock(spec=noCtorArg)
mockSUT.okFunc()
mockSUT.assert_has_calls([mock.call.okFunc()])
# passes
def test_withCtorArg_okWithArgFuncTest(self):
mockSUT = mock.MagicMock(spec=withCtorArg)
mockSUT.okWithArgFunc("testing")
mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")])
# fails
def test_withCtorArg_doNotOkFuncTest(self):
mockSUT = mock.MagicMock(spec=withCtorArg)
mockSUT.notOkFunc()
mockSUT.assert_has_calls([mock.call.notOkFunc()])
if __name__ == '__main__':
unittest.main()
我如何运行测试,输出如下:
E:\work>python -m unittest testCopyFuncWithMock
.F.
======================================================================
FAIL: test_withCtorArg_doNotOkFuncTest (testCopyFuncWithMock.BasicTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "testCopyFuncWithMock.py", line 38, in test_withCtorArg_doNotOkFuncTest
mockSUT.assert_has_calls([mock.call.notOkFunc()])
File "C:\Python27\lib\site-packages\mock\mock.py", line 969, in assert_has_calls
), cause)
File "C:\Python27\lib\site-packages\six.py", line 718, in raise_from
raise value
AssertionError: Calls not found.
Expected: [call.notOkFunc()]
Actual: [call.notOkFunc()]
----------------------------------------------------------------------
Ran 3 tests in 0.004s
FAILED (failures=1)
我正在使用python 2.7.11,通过pip安装了模拟版本2.0.0。
对我做错的任何建议?或者这看起来像图书馆中的错误?
答案 0 :(得分:3)
有趣的是,您选择执行断言的方式掩盖了您的问题。
尝试,而不是:
mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()])
这样做:
mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()], any_order=True)
您将看到实际的例外情况:
TypeError("'obj' parameter lacking default value")
这是因为您尝试实例化具有参数withCtorArg
且没有默认值的类obj
的实例。如果您曾尝试直接实例化它,您会看到:
TypeError: __init__() takes exactly 2 arguments (1 given)
但是,由于您让mock
库处理模拟对象的实例化,因此会发生错误 - 并且您获得TypeError
异常。
修改相关课程:
class withCtorArg:
def __init__(self, obj = None):
pass
def notOkFunc(self):
pass
def okWithArgFunc(self, anArgForMe):
pass
并为obj添加默认的None值解决了这个问题。
答案 1 :(得分:2)
我不认为我可以明确地解释为什么会出现这种情况,我仍然怀疑Mock库中存在一个错误,因为问题只发生在对被调用函数没有参数的测试用例中。感谢advance512指出真正的错误被隐藏了!
但是要解决此问题,无需修改生产代码,我将使用以下方法:
# passes
@mock.patch ('mymodule.noCtorArg')
def test_noCtorArg_okFunc(self, noCtorArgMock):
mockSUT = noCtorArg.return_value
mockSUT.okFunc()
mockSUT.assert_has_calls([mock.call.okFunc()])
# passes
@mock.patch ('mymodule.withCtorArg')
def test_withCtorArg_okWithArgFuncTest(self, withCtorArgMock):
mockSUT = withCtorArg.return_value
mockSUT.okWithArgFunc("testing")
mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")])
# now passes
@mock.patch ('mymodule.withCtorArg')
def test_withCtorArg_doNotOkFuncTest(self, withCtorArgMock):
mockSUT = withCtorArg.return_value
mockSUT.notOkFunc()
mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True)
修改:
这样做的一个问题是模拟没有设置spec
。这意味着SUT能够调用原始类定义中不存在的方法。
另一种方法是将要包装的类包装起来:
class withCtorArg:
def __init__(self,obj):
pass
def notOkFunc(self):
raise NotImplemented
def okWithArgFunc(self, anArgForMe):
raise NotImplemented
class wrapped_withCtorArg(withCtorArg):
def __init__(self):
super(None)
class BasicTestSuite(unittest.TestCase):
"""Basic test Cases."""
# now passes
def test_withCtorArg_doNotOkFuncTest(self):
mockSUT = mock.MagicMock(spec=wrapped_withCtorArg)
mockSUT.notOkFunc()
#mockSUT.doesntExist() #causes the test to fail. "Mock object has no attribute 'doesntExist'"
assert isinstance (mockSUT, withCtorArg)
mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True)