3

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

def get_redis()
  return redis_instance


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


def foo()
  bar("key", value)

在我的测试中,我想模拟函数get_redis以返回 的实例fakeredis.FakeStrictRedis(),所以我这样做了

def test_foo(mocker):
  mocker.patch("app.main.get_redis", return_value=fakeredis.FakeStrictRedis())
  foo()

模拟函数没有效果,该foo函数尝试使用 main 中的 get_redis 函数连接到真正的 redis。

如果我以这种方式写作有效

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

这可行,但我可以将 redis 作为默认值传递。我怎么能嘲笑?

4

2 回答 2

2

我只是bar稍微修改一下函数,以便在应用模拟之前不会调用你的函数:

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

以这种方式编写函数意味着您的模拟将在调用函数时应用,而不是在启动之前应用任何模拟。简而言之,以bar这种方式编写,确保每次调用函数时get_redis它的返回都会传播到你的函数中。bar

于 2020-10-12T04:31:38.130 回答
1

您的模拟没有任何问题,但您似乎误解了默认参数值的工作方式。如Python 语言参考中所述执行函数定义时会评估默认参数值

在您的情况下,这意味着在定义get_redis时已经调用了原始文件:bar

def bar(key, value, redis=get_redis()):

该语句在pytest导入模块时执行,即test_foo执行之前,因此get_redis在测试中模拟没有效果,因为到那时已经太晚了。

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

def bar(key, value, redis=None)
  redis = redis or get_redis()
  redis.set(key, value)
于 2020-10-16T20:51:08.043 回答