【问题标题】:Mocking a function/object, and return values based on input/conditions模拟函数/对象,并根据输入/条件返回值
【发布时间】:2021-10-26 14:02:39
【问题描述】:

我面临以下情况。

我有以下源码

#file:[src/my_module.py]

def order_names(unordered_input: List) -> str: 
   # function that orders a list of names
   ...
       if(is_ID(unordered_input[i])):
           id = unordered_input[i]
           name = get_name_by_id(id)
   ...

def get_name_by_id(id) -> str: 
   # function that returns a name, based on an ID, through a Rest API call
   return make_some_network_call(id)

我想测试函数order_names,我想模拟对get_name_by_id(id)的调用。

假设get_name_by_id(id)会被各种ids多次调用,是否可以创建一个根据输入返回值的mock?

例如:

#file:[test/test_my_module.py]
from unittest import mock
from my_module import order_names

@mock.patch("src.my_module.get_name_by_id", return_value={"3": "Mark", "4": "Kate", "5":"Alfred"})
def test_order_names():
   ordered_names = order_names(["3", "4", "Suzan", "5"])
   assert ordered_names == "Alfred, Kate, Mark, Suzan"

上述测试代码是要实现的行为类型的示例,因为get_name_by_id() 不是dict 返回类型。

干杯!

【问题讨论】:

  • get_name_by_id 应该返回一个名称;嘲笑它以返回 dict 将无济于事。我认为您真正想要模拟的是make_some_network_call,以便get_name_by_id 继续按原样工作。
  • @chepner 没错。我提供的补丁代码只是期望结果的一个例子。让我详细说明一下,因为我可以看到这会造成混乱。
  • 我也会后退一点;可能有理由完全修补 get_name_by_id 而不是修补它使用的东西。
  • @chepner 你能详细说明一下吗?我相信这就是我想要实现的目标。

标签: python python-3.x python-unittest python-mock


【解决方案1】:

您基本上需要get_name_by_id 的替代实现,而不仅仅是一个新的返回值。

# Adjust the definition to behave the same when the lookup fails
def get_name_locally(id):
    return {"3": "Mark", "4": "Kate", "5":"Alfred"}.get(id)


def test_order_names():
    with mock.patch('src.my_module.get_name_by_id', get_name_locally):
        ordered_names = order_names(["3", "4", "Suzan", "5"])
    assert ordered_names = "Alfred, Kate, Mark, Suzan"

如果get_name_by_id 更复杂,您也可以考虑修补网络调用并让get_name_by_id 按原样运行。

# The same as get_name_locally above, but only because
# get_name_by_id and make_some_network_call are functionally
# identical as far as the question is written.
def network_replacement(id):
    return {"3": "Mark", "4": "Kate", "5":"Alfred"}.get(id)


def test_order_names():
    with mock.patch('src.my_module.make_some_network_call', network_replacement):
        ordered_names = order_names(["3", "4", "Suzan", "5"])
    assert ordered_names = "Alfred, Kate, Mark, Suzan"

现在,当您调用order_names,而它又调用get_name_by_idmake_some_network_call 的替代定义将被get_name_by_id 使用。

【讨论】:

  • 能否以某种方式修补网络调用,从而为ids 的各种输入提供不同的结果?
  • 是的。鉴于您最初可以定义get_name_by_id = make_some_network_call,您可以使用相同的get_name_locally,只是修补一个不同的名称。本质上,您只是用本地 dict 替换网络上存在的任何数据库。
猜你喜欢
  • 2019-10-27
  • 1970-01-01
  • 2020-08-22
  • 2020-08-23
  • 2022-07-25
  • 1970-01-01
  • 2012-04-26
  • 2022-12-03
  • 2019-02-09
相关资源
最近更新 更多