在python中严格模拟

时间:2014-07-08 11:32:06

标签: python unit-testing mocking python-mock

python中有没有相当于严格的模拟?报告模拟方法的非预期调用的一些机制(本例中为action.step2()),就像在GoogleMock框架中一样。

class Action:
    def step1(self, arg):
        return False

    def step2(self, arg):
        return False

def algorithm(action):
    action.step1('111')
    action.step2('222')
    return True

class TestAlgorithm(unittest.TestCase):
    def test_algorithm(self):
        actionMock = mock.create_autospec(Action)
        self.assertTrue(algorithm(actionMock))
        actionMock.step1.assert_called_once_with('111')

3 个答案:

答案 0 :(得分:4)

看起来不支持开箱即用。但是,至少有两种方法可以达到相同的效果。

传递允许成员列表

根据模拟文档

  

spec :这可以是字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果传入一个对象,则通过在对象上调用dir来形成字符串列表(不包括不受支持的魔术属性和方法)。 访问此列表中没有的任何属性会引发AttributeError

因此,为了使测试示例失败,只需替换

actionMock = mock.create_autospec(Action)

actionMock = mock.Mock(spec=['step1'])

与将类或实例作为spec参数传递相比,这种方法有一定的缺点,因为您必须传递所有允许的方法,而不是设置它们的期望,有效地将它们注册两次。此外,如果您需要限制方法的子集,则必须传递除这些方法之外的所有方法的列表。这可以通过以下方式实现:

all_members = dir(Action)  # according to docs this is what's happening behind the scenes
all_members.remove('step2')  # remove all unwanted methods 
actionMock = mock.Mock(spec=all_members)

设置限制方法的例外

替代方法是在你不想被调用的方法上明确地设置失败:

def test_algorithm(self):
    actionMock = mock.create_autospec(Action)
    actionMock.step2.side_effect = AttributeError("Called step2") # <<< like this
    self.assertTrue(algorithm(actionMock))
    actionMock.step1.assert_called_once_with('111')

这也有一些限制:你必须设置错误和期望。

作为最后一点,该问题的一个根本解决方案是修补mock以将strict参数添加到Mock构造函数并发送拉取请求。它要么被接受,要么mock维护者会指出如何实现这一目标。 :)

答案 1 :(得分:0)

是的,可以使用spec=autospec=参数。有关详细信息,请参阅mock documentation on Autospeccing。在你的例子中,它将成为:

action_mock = mock.Mock(spec=Action)

或:

action_mock = mock.Mock('Action', autospec=True)

答案 2 :(得分:0)

另一种可能性:

单独检查受限方法的call_count

确保call_count对于不应调用的方法为零。

class TestAlgorithm(unittest.TestCase):
    def test_algorithm(self):
        actionMock = mock.create_autospec(Action)
        self.assertTrue(algorithm(actionMock))
        actionMock.step1.assert_called_once_with('111')
        self.assertEqual(actionMock.step2.call_count, 0) # <<< like this

缺点是你必须逐个检查所有意外的电话。