【发布时间】:2022-11-11 02:25:59
【问题描述】:
最终目标:我希望能够在 pytest 中快速模拟 input() 内置函数,并将其替换为生成(变量)字符串列表的迭代器。这是我当前的版本,有效:
from typing import Callable
import pytest
def _create_patched_input(str_list: list[str]) -> Callable:
str_iter = iter(str_list.copy())
def patched_input(prompt: str) -> str: # has the same signature as input
val = next(str_iter)
print(prompt + val, end="\n"),
return val
return patched_input
@pytest.fixture
def _mock_input(monkeypatch, input_string_list: list[str]):
patched_input = _create_patched_input(input_string_list)
monkeypatch.setattr("builtins.input", patched_input)
def mock_input(f):
return pytest.mark.usefixtures("_mock_input")(f)
# Beginning of test code
def get_name(prompt: str) -> str:
return input(prompt)
@mock_input
@pytest.mark.parametrize(
"input_string_list",
(["Alice", "Bob", "Carol"], ["Dale", "Evie", "Frank", "George"]),
)
def test_get_name(input_string_list):
for name in input_string_list:
assert get_name("What is your name?") == name
但是,由于以下几个原因,这感觉不完整:
- 要求
parameterize调用中的参数名称为input_string_list,感觉比较脆。 - 如果我将灯具移动到另一个函数中,我需要同时导入
mock_input和_mock_input。
对我来说正确的是拥有一个可以像@mock_input(strings)一样使用的装饰器(工厂),这样你就可以像这样使用它
@mock_input(["Alice", "Bob", "Carol"])
def test_get_name():
....
或者,更符合我的用例,
@pytest.mark.parametrize(
"input_list", # can be named whatever
(["Alice", "Bob", "Carol"], ["Dale", "Evie", "Frank", "George"]),
)
@mock_input(input_list)
def test_get_name():
....
后者我认为您做不到,因为 pytest 不会将其识别为夹具。最好的方法是什么?
【问题讨论】:
-
我宁愿在
_mock_input夹具上使用间接参数化,因为它无论如何都需要参数。 -
@hoefling 你能说明你的意思吗?
标签: python mocking pytest decorator