【问题标题】:Strict mock in pythonpython中的严格模拟
【发布时间】:2014-08-29 02:58:00
【问题描述】:

在 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')

【问题讨论】:

  • 您使用哪个版本的 Python?看来您的代码可以使用 Python 3 中的 unittest.mock 按原样工作。
  • 您的意思是“未明确设置的任何呼叫”是“意外呼叫”吗?
  • @Cilyan 抱歉不准确。此示例使用 python 2.7.6 进行测试。但是我在 python 3.4.0 中得到了相同的行为,其中 unittest 在标准库中。
  • @J0HN 是的。当方法被调用,但没有人在测试体中预测时,该方法将被调用。在这种情况下,StrictMock (GoogleMock) 将使测试失败。

标签: python unit-testing mocking python-mock


【解决方案1】:

另一种可能性:

在受限方法上单独检查 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

缺点是你必须一一检查所有意外调用。

【讨论】:

    【解决方案2】:

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

    通过的允许成员列表

    根据模拟文档

    规范:这可以是字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果您传入一个对象,则通过在该对象上调用 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 维护者会指出如何实现这一点。 :)

    【讨论】:

      【解决方案3】:

      是的,这可以使用spec=autospec= 参数来实现。有关更多信息,请参阅mock documentation on Autospeccing。在您的示例中,它将变为:

      action_mock = mock.Mock(spec=Action)
      

      或:

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

      【讨论】:

      • 感谢您的回答,但我认为这不是我要找的。据我所知,规范/自动规范仅在我们对未由真实类实现的模拟方法做出一些“期望”时(即当我们有简单的错字时)保护我。我真正想要的是防止出现以下情况:代码调用了一些模拟方法,但没有人在测试主体中明确预测这一点。
      猜你喜欢
      • 2011-03-09
      • 2014-07-02
      • 2021-09-07
      • 1970-01-01
      • 2011-03-12
      • 2017-06-17
      • 2011-12-21
      • 1970-01-01
      • 2016-11-29
      相关资源
      最近更新 更多