You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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 == ""

方案说明

  1. 连续输入模拟:通过迭代器一次性提供所有测试输入,函数会自动递归处理每个无效输入,直到遇到有效值返回,无需多次调用get_date。
  2. 返回值验证:直接断言最终返回的日期与预期一致,这是函数的核心功能目标。
  3. 错误输出验证:利用capsys fixture捕获打印的错误提示,确保每个无效输入都触发了正确的提示信息,覆盖函数的异常处理逻辑。

内容的提问来源于stack exchange,提问作者Sérgio Pedro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 00:05:37