2016-06-20 34 views
4

我遇到了一个问题,我认为这可能是我使用的库的一个错误。不过,我对python,unittest和unittest.mock库相当陌生,所以这可能只是我理解中的一个漏洞。生产类构造函数需要额外参数时,为什么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

不太清楚你的具体问题,但只是一个Python编码注 - Python2.7类,你总是要继承'对象',即'class WithCtorArg(object):'。此外,类名通常以大写字母开头(尽管我意识到可能刚刚在翻译成您的MCVE时迷失了)。 – dwanderson

+0

谢谢@dwanderson。 –

回答

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没有发def lt值。如果你曾试图居然直接实例化它,你会看到:

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增加一个默认值没有解决的问题。

+0

是的,好点。我希望通过使用模拟,你不需要调用实际的构造函数,因为它与SUT无关。不幸的是,在现实世界的例子中(至少在我的例子中)修改被嘲笑的类不是一个好主意,构造函数参数是有原因的,并且不是可选的,还有其他建议吗? –

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) 
+0

试图实例化一个在其构造函数中需要参数的类的实例,而不给它参数,如果你试图直接执行它,将会失败。你为什么期望它在模拟工作,特别是MagicMock中自动“包装”各种方法并监视你所做的调用以及它们是否有效? :)我会说,你看到的行为是有道理的,除了库中的错误。 – advance512

+0

我希望这个库会包装构造函数(就像我在这个答案中所做的那样),因为我们并不真的想要一个类的实例。我们只需要假装一个实际上除了对所做的调用进行间谍以外的任何事情,并假冒返回值。在这种情况下,我们并不关心构造函数签名是否匹配,因为它将在SUT之外创建并注入。 –

+0

或者,如果在创建模拟时有一种方法可以提供构造函数参数,那么它们可以被转发给类构造函数。你知道如何用这个模拟库来做到这一点吗?我没有发现任何有关创建具有构造函数参数的类的mock的文档。感谢您的关注。 –

相关问题