【问题标题】:Python-mock: how to test if super() was calledPython-mock:如何测试是否调用了 super()
【发布时间】:2014-08-05 15:26:09
【问题描述】:

我有以下结构:

class A(Object):
    def method(self):
        return 'a'

class B(A):
   def __init__(self, test):
       self.test = test

   def method(self):
        if self.test:
           return super(A, self).method(self)
        else:
           return 'b'

我想做的是编写一个测试用例,测试如果 self.test 为真,则调用超级函数并调用 A 类的方法函数。

我怎样才能做到这一点?我应该模拟什么?

进阶 Q:如果 A 类和 B 类在一个单独的模块中,并且它们具有相同的名称,该怎么办? 所以我会写而不是B类:A类(module1.A): 这会改变嘲笑吗?

【问题讨论】:

  • 你不能给A.method打补丁吗? ... with mock.patch.object(A, 'method'): ... 你不应该关心它是如何被调用的(例如,通过 super 或其他方式......)重要的是它被调用的
  • 您的测试应该只关心method 返回'a''b'。该方法如何实现这一点不是您的测试应该关心的。如果将来您想重构 B.method() 并且它不再使用 super(),那么如果它的行为仍然相同,那么您的方法同样有效。
  • @MartijnPieters 如果您不模拟对外部类的调用,那不就是一种集成测试吗?我有一种感觉(来自 Sandi Metz 的谈话),你所说的只有在处理对私有方法的调用时才是正确的(不要模拟它们,只是断言最终的公共副作用或返回值)。但是,对内部公共方法的模拟调用确实会创建对实现的依赖,所以我不确定了。
  • @MartijnPieters 我刚刚再次查看了 Metz 的演讲,我认为我部分错了。在查询消息的情况下,我们只对方法最后返回的内容感兴趣(不管它在内部调用什么)。如果消息不是查询而是命令(它改变了某些状态),我们有兴趣断言实例上的公共副作用,或模拟对外部实例的调用。但我觉得我仍然缺少一些东西。我们是否应该模拟对内部公共方法的调用,因为我们将分别对它们进行单元测试?演讲链接:youtube.com/watch?v=URSWYvyc42M
  • @KurtBourbaki:在实际的日常工作中:您总是可以稍后决定是否需要将被测单元分解为更小的单元,如果这有助于增加信心或回归已找到您要测试的。测试是一种工具,而不是最终目标,它服务于软件开发速度。不要让它陷入困境!

标签: python unit-testing mocking


【解决方案1】:

正如@MartijnPieters 指出的那样,测试 super 被调用通常是对实现细节的测试,而不是对合约的测试。尽管如此,测试特定的实现可能仍然是可取的——或者父类可能有一个需要调用 super 的协定。要测试是否调用了 super,请使用模拟,如 @mgilson 所述。详细答案:

import mock, unittest

class TestB(unittest.TestCase):

    @mock.patch("A.method")
    def test_super_method(self, mock_super):
        B(True).method()
        self.assertTrue(mock_super.called)

    @mock.patch("A.method")
    def test_super_method(self, mock_super):
        B(False).method()
        self.assertFalse(mock_super.called)

您需要在补丁中指定完整的命名空间。例如,@mock.patch("module.submodule.A.method")。这可以对A中的任何方法进行,包括__init__;语法完全一样。

【讨论】:

    【解决方案2】:

    以@Malina 的出色答案为基础,如果您无法修补模块 A 上的方法(例如,A 在第三方库中,是令人讨厌的遗留系统的一部分,在运行时动态创建等),您可以仍然通过在 B 类上显式创建另一个方法来测试此功能以进行测试。

    修改类B

    import A
    
    class B(A):
       def __init__(self, test):
           self.test = test
    
       def method(self):
            if self.test:
               self._delegate_to_A()  # Move the super call to a separate method
            else:
               return 'b'
       
       def _delegate_to_A(self)
           return super(A, self).method(self)
    

    修改了B类的测试代码

    import mock, unittest
    
    class TestB(unittest.TestCase):
    
        # Patch B's `delegate` method instead of A
        @mock.patch("B._delegate_to_A")
        def test_super_method(self, mock_super):
            B(True).method()
            self.assertTrue(mock_super.called)
    
        @mock.patch("B._delegate_to_A")
        def test_super_method(self, mock_super):
            B(False).method()
            self.assertFalse(mock_super.called)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-04
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      相关资源
      最近更新 更多