如何用pytest测试递归实现的get_date函数?求最优测试方案
问题
我在项目中有一个名为get_date的递归函数,需要用pytest测试,函数实现如下:
def get_date(string): try: date_str = input(string) date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date() return date except ValueError: print("Please enter a date in the YYYY-MM-DD format") return get_date(string)
之前的while循环版本会抛出ValueError导致崩溃,所以改成了递归版。我写的测试用例如下:
def test_get_date(monkeypatch): inputs = [ "12/06/2023", "12-06-2023", "2023/06/12", "2023/17/87", "hello", "2023-06-12", ] input_values = iter(inputs) monkeypatch.setattr("builtins.input", lambda _: next(input_values)) # Test first input ("12/06/2023") with pytest.raises(ValueError): get_date("Date (YYYY-MM-DD): ") # Test second input ("12-06-2023") with pytest.raises(ValueError): get_date("Date (YYYY-MM-DD): ") # Test third input ("2023/06/12") with pytest.raises(ValueError): get_date("Date (YYYY-MM-DD): ") # Test fourth input ("hello") with pytest.raises(ValueError): get_date("Date (YYYY-MM-DD): ") # Test fifth output ("2023-06-12") assert get_date("Date (YYYY-MM-DD): ") == datetime.date(2023, 6, 12)
但递归版不会抛出异常,原测试用例失效。我不想修改函数结构(比如改成返回布尔值的is_date函数),现有功能运行正常。我用迭代器模拟输入,但不确定是否最优,希望得到合适的测试方法。
解决方案
问题根源
递归版get_date在捕获ValueError后直接递归调用自身,不会将异常抛出到函数外部,因此原测试中pytest.raises(ValueError)的断言永远无法触发,导致测试失效。
优化测试方案
用迭代器模拟输入的思路是合理的,只需调整测试逻辑,聚焦于最终返回值和错误输出的验证,无需尝试捕获外部异常。
完整测试代码
import pytest import datetime def get_date(string): try: date_str = input(string) date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date() return date except ValueError: print("Please enter a date in the YYYY-MM-DD format") return get_date(string) def test_get_date_handles_invalid_inputs(monkeypatch, capsys): # 按顺序提供无效输入,最后是有效输入 test_inputs = [ "12/06/2023", "12-06-2023", "2023/06/12", "2023/17/87", "hello", "2023-06-12", ] input_iterator = iter(test_inputs) # 替换系统input函数,每次返回迭代器的下一个值 monkeypatch.setattr("builtins.input", lambda _: next(input_iterator)) # 调用函数,函数会自动处理所有无效输入,直到返回有效日期 returned_date = get_date("Date (YYYY-MM-DD): ") # 验证最终返回的日期正确 assert returned_date == datetime.date(2023, 6, 12) # 验证错误提示输出次数:每个无效输入对应一次提示 captured_output = capsys.readouterr() expected_error = "Please enter a date in the YYYY-MM-DD format\n" * 5 assert captured_output.out == expected_error def test_get_date_returns_valid_input_immediately(monkeypatch, capsys): # 模拟第一次输入就是有效日期 monkeypatch.setattr("builtins.input", lambda _: "2023-12-25") returned_date = get_date("Date (YYYY-MM-DD): ") assert returned_date == datetime.date(2023, 12, 25) # 无错误输出 captured_output = capsys.readouterr() assert captured_output.out == ""
方案说明
- 连续输入模拟:通过迭代器一次性提供所有测试输入,函数会自动递归处理每个无效输入,直到遇到有效值返回,无需多次调用
get_date。 - 返回值验证:直接断言最终返回的日期与预期一致,这是函数的核心功能目标。
- 错误输出验证:利用
capsysfixture捕获打印的错误提示,确保每个无效输入都触发了正确的提示信息,覆盖函数的异常处理逻辑。
内容的提问来源于stack exchange,提问作者Sérgio Pedro
相关产品推荐
相关产品推荐

