简体   繁体   English

如何修补 python @classmethod 以调用我的 side_effect 方法?

[英]How do I patch a python @classmethod to call my side_effect method?

The following code shows the problem.下面的代码显示了这个问题。

I can successfully patch object instance and static methods of this SomeClass我可以成功地修补这个SomeClass的对象实例和静态方法

However, I can't seem to be able to patch classmethods.但是,我似乎无法修补类方法。

Help much appreciated!非常感谢帮助!

from contextlib import ExitStack
from unittest.mock import patch


class SomeClass:
    def instance_method(self):
        print("instance_method")

    @staticmethod
    def static_method():
        print("static_method")

    @classmethod
    def class_method(cls):
        print("class_method")

# --- desired patch side effect methods ----
def instance_method(self):
    print("mocked instance_method")

def static_method():
    print("mocked static_method")

def class_method(cls):
    print("mocked class_method")

# --- Test ---
obj = SomeClass()

with ExitStack() as stack:
    stack.enter_context(
        patch.object(
            SomeClass,
            "instance_method",
            side_effect=instance_method,
            autospec=True
        )
    )
    stack.enter_context(
        patch.object(
            SomeClass,
            "static_method",
            side_effect=static_method,
            # autospec=True,
        )
    )
    stack.enter_context(
        patch.object(
            SomeClass,
            "class_method",
            side_effect=class_method,
            # autospec=True
        )
    )


    # These work
    obj.instance_method()
    obj.static_method()

    # This fails with TypeError: class_method() missing 1 required positional argument: 'cls'
    obj.class_method()

General solution通用解决方案

A way to patch a classmethod would be to use new=classmethod(class_method) instead of side_effects=class_method .修补类方法的一种方法是使用new=classmethod(class_method)而不是side_effects=class_method
This works pretty well in general.这在一般情况下工作得很好。

Downside缺点

Using new , the patched object isn't necessarily an instance of Mock , MagicMock , AsyncMock or PropertyMock anymore (During the rest of the answer i'll only reference Mock as all the others are subclasses of it).使用new ,修补后的对象不再是MockMagicMockAsyncMockPropertyMock的实例(在剩下的回答中我将只引用Mock因为所有其他都是它的子类)。
It is only then an instance of these when you explicitly specify it to be one via eg new=Mock(...) or ommit the attribute completely.只有当您通过例如new=Mock(...)明确指定它是一个或完全省略该属性时,它才是这些的一个实例。
That wouldn't be the case with the solution provided at the top of this answer.此答案顶部提供的解决方案并非如此。 So when you try to eg check if the function already got called using obj.class_method.assert_called() , it'll give an error saying that function has no attribute assert_called which is caused by the fact that the patched object isn't an instance of Mock , but instead a function .因此,当您尝试检查函数是否已使用obj.class_method.assert_called()调用时,它会给出一个错误,指出该function has no attribute assert_called ,这是由于修补对象不是实例引起的的Mock ,而是一个function

Unfortunately I don't see any solution to this downside in that scenario at the moment不幸的是,目前我没有看到任何解决方案来解决这种情况

Concluded differences between new and side_effect : newside_effect之间的结论差异:

  • new specifies what object to patch the target with (doesn't necessarily have to be an instance of Mock ) new指定用什么对象来修补目标(不一定是Mock的实例)
  • side_effect specifies the side_effect of the Mock instance that gets created when using patch without new Also they don't play very well together, so only one of these can/should be used in the same patch(...) . side_effect指定在不使用new的情况下使用 patch 时创建的Mock实例的 side_effect 而且它们不能很好地协同工作,因此只能/应该在同一个patch(...)中使用其中一个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM