修补函数的 __call__

2022-01-08 00:00:00 python mocking time python-mock

问题描述

我需要在测试中修补当前日期时间.我正在使用这个解决方案:

I need to patch current datetime in tests. I am using this solution:

def _utcnow():
    return datetime.datetime.utcnow()


def utcnow():
    """A proxy which can be patched in tests.
    """
    # another level of indirection, because some modules import utcnow
    return _utcnow()

然后在我的测试中,我会执行以下操作:

Then in my tests I do something like:

    with mock.patch('***.utils._utcnow', return_value=***):
        ...

但今天我想到了一个想法,我可以通过修补函数 utcnow__call__ 来简化实现,而不是额外添加一个 _utcnow.

But today an idea came to me, that I could make the implementation simpler by patching __call__ of function utcnow instead of having an additional _utcnow.

这对我不起作用:

    from ***.utils import utcnow
    with mock.patch.object(utcnow, '__call__', return_value=***):
        ...

如何优雅地做到这一点?

How to do this elegantly?


解决方案

当你修补一个函数的 __call__ 时,你是在设置那个 的 __call__ 属性实例.Python 实际上调用了类上定义的 __call__ 方法.

When you patch __call__ of a function, you are setting the __call__ attribute of that instance. Python actually calls the __call__ method defined on the class.

例如:

>>> class A(object):
...     def __call__(self):
...         print 'a'
...
>>> a = A()
>>> a()
a
>>> def b(): print 'b'
...
>>> b()
b
>>> a.__call__ = b
>>> a()
a
>>> a.__call__ = b.__call__
>>> a()
a

将任何东西分配给 a.__call__ 是没有意义的.

Assigning anything to a.__call__ is pointless.

但是:

>>> A.__call__ = b.__call__
>>> a()
b

TLDR;

a() 不调用 a.__call__.它调用 type(a).__call__(a).

TLDR;

a() does not call a.__call__. It calls type(a).__call__(a).

回答为什么type(x).__enter__(x) 而不是 Python 标准 contextlib 中的 x.__enter__()?".

There is a good explanation of why that happens in answer to "Why type(x).__enter__(x) instead of x.__enter__() in Python standard contextlib?".

特殊方法查找的 Python 文档中记录了此行为.

相关文章