【问题标题】:Mock default parameter value in inner function内部函数中的模拟默认参数值
【发布时间】:2020-10-09 18:06:55
【问题描述】:

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

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 作为默认值传递。如何模拟?

【问题讨论】:

  • 我根本不会嘲笑。 foo 需要一个 Redis 实例,如果只是传递给bar,所以将其设为参数。然后你可以简单地将你的假实例作为参数传递,而不是修补任何东西。
  • 我无法将 redis 实例传递给 foo,因为它是 RPC 方法
  • 我猜另一种方法是创建一个redis的全局变量。

标签: python python-unittest python-mock pytest-mock


【解决方案1】:

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

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

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

【讨论】:

  • 这不起作用,它仍然调用原始的get_redis,因为默认值仍然计算为原​​始函数。
【解决方案2】:

您的模拟没有任何问题,但您似乎误解了默认参数值的工作原理。正如the Python Language Reference 中所解释的,默认参数值在函数定义被执行时进行评估。

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

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)

【讨论】:

    猜你喜欢
    • 2015-09-09
    • 2015-07-08
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 2011-04-09
    • 1970-01-01
    相关资源
    最近更新 更多