Python 如何修补类方法以便可以访问 cls 变量

问题描述 投票:0回答:2

我有兴趣使用 cls 的值和其他参数来修补 Schema 类中名为 _validate 的类方法以及替换的 fn 。

对于上下文,ArrayHoldingAnyType 继承自 Schema,实例化时会调用 _validate。 当我使用下面的代码尝试时,cls 的值不是一个类。如何修复 cls 变量?

    def test_validate_called_n_times(self):
        def replacement_validate(cls, *args):
            # code which will return the correct values

        with patch.object(Schema, '_validate', new=replacement_validate) as mock_validate:
            path_to_schemas = ArrayHoldingAnyType(['a'])
            # I will check that the mock was called a certain number of times here with specific inputs

python-3.x mocking patch
2个回答
1
投票

所以这里的问题是 replacement_validate 中缺少 classmethod 装饰器。 这解决了它:

    def test_validate_called_n_times(self):
        @classmethod
        def replacement_validate(cls, *args):
            # code which will return the correct values

        with patch.object(Schema, '_validate', new=replacement_validate) as mock_validate:
            path_to_schemas = ArrayHoldingAnyType(['a'])
            # I will check that the mock was called a certain number of times here with specific inputs

0
投票

也许它对某人有帮助 - 这是我迄今为止所取得的成就(使用@spacether答案):

class A:
    @classmethod
    def f(cls, *args, **kwargs):
        print('Original A.f() called with', cls, args, kwargs)


with mock.patch.object(
        A,
        'f',
        new=classmethod(mock.MagicMock(side_effect=A.f.__func__))
) as patched:
    A.f(1, 2)

print(patched)
print(patched.__func__)
print(patched.__func__.call_args)

输出是

Original A.f() called with <class '__main__.A'> (1, 2) {}
<classmethod(<MagicMock id='5089796880'>)>
<MagicMock id='5089796880'>
call(<class '__main__.A'>, 1, 2)

--所以返回的修补对象是一个

classmethod
类型对象,为了获取模拟,我们需要访问它的
__func__
属性。

这里还有一个更方便的版本,作为一个单独的函数,它接受一个可选的可调用对象并返回一个带有

.mock
属性的包装器:

def patch_classmethod(class_, method_name, new_method=None):
    """by default - call original method; to get the mock object please use .mock attribute of the returned object"""
    if new_method is None:
        new_method = getattr(class_, method_name).__func__

    class ClassmethodMockContainer(classmethod):
        @property
        def mock(self) -> mock.Mock:
            return self.__func__

    mocked_method: Callable = mock.MagicMock(side_effect=new_method)
    return mock.patch.object(class_, method_name, new=ClassmethodMockContainer(mocked_method))


with patch_classmethod(A, 'f') as patched:
    A.f(1, 2)

print(patched)
print(patched.mock)
print(patched.mock.call_args)


with patch_classmethod(
        A,
        'f',
        new_method=lambda cls, *a, **kw: print('hello', cls, a, kw),
) as patched:
    A.f(1, 2)

print(patched)
print(patched.mock)
print(patched.mock.call_args)

输出:

Original A.f() called with <class '__main__.A'> (1, 2) {}
<classmethod(<MagicMock id='5194966864'>)>
<MagicMock id='5194966864'>
call(<class '__main__.A'>, 1, 2)

hello <class '__main__.A'> (1, 2) {}
<classmethod(<MagicMock id='5178984080'>)>
<MagicMock id='5178984080'>
call(<class '__main__.A'>, 1, 2)
© www.soinside.com 2019 - 2024. All rights reserved.