【问题标题】:Mocking the super class calls on python在 python 上模拟超类调用
【发布时间】:2012-10-17 02:36:30
【问题描述】:

我正在做一些单元测试,有时我需要模拟一个super 调用以引发错误,例如:

@classmethod
def myfunc(cls, *args, **kwargs)
    try:
        super(MyClass, cls).my_function(args, kwargs)
    except MyException as e:
        #...

我一般使用mocker 库来模拟我的对象,但我还没有找到模拟它的方法。

【问题讨论】:

    标签: python unit-testing mocking


    【解决方案1】:

    好吧,那你需要mock MyClass 的超类的my_function 方法才能炸掉。

    【讨论】:

    • 我知道我可以在那里模拟一些东西来炸毁,我只是好奇如何模拟super 类调用,这应该是可能的。谢谢你的想法。
    • 您的意思是模拟 super 可调用本身?我认为这会导致在更多地方引发异常。
    • 是的,我知道这不是一件小事:s
    【解决方案2】:

    我找到了一种方法,有点老套,但它有效,我会用我的例子来解释,这是基于this 响应,所以感谢@kindall:

    def my_test(self):
        import __builtin__
        from mocker import Mocker, KWARGS, ARGS
    
        mymocker = mocker.mock()
        mymocker.my_function(ARGS, KWARGS)
        mocker.throw(MyException)
    
        def mysuper(*args, **kwargs):
            if args and issubclass(MyClass, args[0]):
                return mymocker
            return original_super(*args, **kwargs)
    
        __builtin__.original_super = super
        __builtin__.super = mysuper
    
        with mocker:
            MyClass.myfunc()
    

    所以基本上我要做的是检查super 调用是否来自我要模拟的类,否则只需执行正常的super

    希望这可以帮助某人:)

    【讨论】:

      【解决方案3】:

      使用标准库中的unittest.mock 我会做这样的事情。

      在你的类定义中:

      from somelib import ASuperClass
      
      class MyClass(ASuperClass):
          def my_cool_method(self):
              return super().my_cool_method()
      

      在你调用MyClass的模块中:

      from unittest.mock import patch
      from mymodule import MyClass
      
      @patch("mypackage.mymodule.ASuperClass.my_cool_method")
      def call_with_mock(mocked_super):
          myinstance = MyClass()
          myinstance.my_cool_method()
          # do stuff with `mocked_super`
      
      call_with_mock()
      

      【讨论】:

      • 我遇到的问题是我有 lotmy_cool_methods 来模拟,它变得笨拙。我需要一种方法来模拟整个类而不是单个方法。
      • 当然不能。模拟一个超类会使你的类成为一个模拟的子类,例如它变成一个模拟并且它失去了你想要测试的所有方法。我最终创建了一个类,该类将另一个类作为输入,在其自身上创建所有匹配的方法,作为模拟(所以它是一个真实的类,其中包含与另一个类的方法匹配的模拟方法)。然后我覆盖了我要测试的类的基类,我可以调用我的方法并确认使用正确的参数调用了正确的超类方法——并在必要时引发异常。
      • 不,该链接与我所说的无关。如果我有一个class A: 和一个class B(A): 并且我想在模拟从A 继承的方法时测试B,你不能只模拟A,因为B 继承自A 和因此它本身变成了一个模拟,这阻止了我调用我想调用的方法,因为它们被模拟了。这适用于我的用例:bazaar.launchpad.net/~cupstream2distro-maintainers/…
      • 看来您无法两全其美。如果您想在不模拟子类的情况下模拟出超类中的所有方法,则必须逐个模拟方法
      • 鉴于您正在为 units 编写测试,逐个模拟每个方法不是更合适吗?在我看来,这似乎是一种更好的方法,可以准确地模拟为了测试单元而必须模拟的内容。
      【解决方案4】:

      @Markus 找对地方了。只要您进行单元测试(即只有一次调用super),您就可以模拟__builtin__.super,如下所示:

      with mock.patch('__builtin__.super') as mock_super:
          mock_super.side_effect = TypeError
          with self.assertRaises(TypeError):
              obj.call_with_super()
      

      【讨论】:

        【解决方案5】:

        如果有人需要另一种方法来解决这个模拟:

        # some_package/some_module.py
        
        class MyClass(SuperClass):
        
            def some_function(self):
                result_super_call = super().function()
        
        
        # test_file.py
        
        @patch('some_package.some_module.super')
        def test_something(self, mock_super):
            obj = MyClass()
            mock_super().some_function.return_value = None
        

        使用 Python 3.6

        【讨论】:

        • 我运行test_something() 并得到“AttributeError: 没有属性'super'”。你能再解释一下吗?
        • @mikerodent,'super' 是类所在模块的属性,而不是类。而不是“some_package.some_module.some_class”,使用“some_package.some_module”。
        【解决方案6】:

        Python 自己的 Mock 类 provides a spec argument 应该对此有所帮助:

        with mock.patch('...ParentClass.myfunc') as mocked_fn:
            mocked_fn.side_effect = MyException()  # Parent's method will raise
            instance = mock.Mock(spec=MyClass)  # Enables using super()
            MyClass.myfunc(instance)  # Will enter your `except` block
        

        【讨论】:

          猜你喜欢
          • 2023-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多