Python 内部函数中的模拟默认参数值

Python 内部函数中的模拟默认参数值,python,python-unittest,python-mock,pytest-mock,Python,Python Unittest,Python Mock,Pytest Mock,我有以下(简化的)代码: def get_redis() 返回redis_实例 定义栏(键、值、redis=get_redis()) redis.set(键、值) def foo() 条(“键”,值) 在我的测试中,我想模拟函数get\u redis来返回fakeredis.fakestritredis()的实例,所以我这样做了 def test_foo(mocker): mocker.patch(“app.main.get_redis”,return_value=fakeredis.Fake

我有以下(简化的)代码:

def get_redis()
返回redis_实例
定义栏(键、值、redis=get_redis())
redis.set(键、值)
def foo()
条(“键”,值)
在我的测试中,我想模拟函数
get\u redis
来返回
fakeredis.fakestritredis()
的实例,所以我这样做了

def test_foo(mocker):
mocker.patch(“app.main.get_redis”,return_value=fakeredis.FakeStrictRedis())
foo()
模拟函数无效,
foo
函数尝试使用main中的get\u redis函数连接到real redis

如果我用这种方式写的话

def条(键、值)
redis=get_redis()
redis.set(键、值)

这是可行的,但我可以将redis作为默认值传递。如何进行模拟?

我只需对
函数稍作修改,如下所示,以便在应用模拟之前不会调用您的函数:

def bar(key, value, redis=get_redis)
    if callable(redis):
        redis = redis()
    redis.set(key, value)

以这种方式编写函数意味着您的模拟将在调用函数时应用,而不是在启动时应用任何模拟。简而言之,以这种方式编写
bar
可以确保
get\u redis
的返回,它将在每次调用函数时通过
bar
函数进行传播。

模拟没有问题,但您似乎误解了默认参数值的工作方式。如中所述,在执行函数定义时计算默认参数值

在您的情况下,这意味着在定义了
bar
时,已经调用了原始的
get\u redis

定义栏(键、值、redis=get_redis()): 当
pytest
导入您的模块时,即在执行
test\u foo
之前执行此语句,因此在测试中模拟
get\u redis
没有效果,因为那时已经太晚了

要使默认工厂函数可模拟,请使用
None
作为默认值,并使函数调用工厂,除非在调用中指定了另一个值:

def条(键、值、redis=None)
redis=redis或get_redis()
redis.set(键、值)

我根本不会嘲笑你
foo
需要一个Redis实例,如果只是传递到
bar
,那么将其作为一个参数。然后你可以简单地将你的假实例作为参数传递,而不是修补任何东西。我不能将redis实例传递给
foo
,因为这是一种RPC方法。我想,另一种方法是将redis作为全局变量。这不起作用,它仍然调用原始的
get\u redis
,因为默认值的计算结果仍然是原始函数。