5

我遇到了一个问题,我认为这可能是我正在使用的库的错误。但是,我对 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。

关于我做错了什么有什么建议吗?或者这看起来像图书馆中的错误?

4

2 回答 2

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 值解决了这个问题。

于 2016-06-20T14:07:09.110 回答
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)
于 2016-06-21T03:25:10.590 回答