如何使用pytest测试get_date函数?

huangapple go评论63阅读模式
英文:

How do I test a get_date function using pytest?

问题

我有一个名为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)


我之前有:

def get_date(string):
while True:
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")
raise


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)

这个问题是,当引发ValueError时,程序会崩溃,而不是重新提示用户输入新的内容,这是绝对不能发生的。

我考虑将该函数更改为一个is_date函数,该函数将返回一个布尔值,但这将涉及更改整个程序,而我不打算这样做,因为一切都运行得很正常。

此外,这是一个无返回值的函数,因此我无法使用通常的assert方法。我做了一些研究,发现了这种iter方法,但我不确定这是否是最佳方法。

我真的希望您能帮助我解决这个问题
英文:

I got this function called get_date in a project and I'm requested to test it via 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)

I previously had:

def get_date(string):
    while True:
        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")
            raise
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)

The issue with this is that when the ValueError is raised the program crashes instead of re-prompting the user for new input, which is something that really can't happen.

I thought of changing the function to a is_date function that would return a bool, but that would involve changing the whole program, which is something I'm not looking for since everything works just fine.

Also, this is a void function and because of it I can't use the usual assert method. I did some research and found this iter method which I'm not sure is the optimal way to do so.

I really hope you can help me solve this problem

答案1

得分: 1

通过添加raise,你会在打印错误消息后重新引发ValueError,从而导致程序崩溃。

因此,如果你希望程序要求用户重新输入数据,需要在get_date函数中删除raise语句。

希望这有所帮助

以下是整个代码:

import datetime
import pytest

def get_date(string):
    while True:
        try:
            date_str = input(string)
            date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
            return date
        except ValueError:
            print("请以YYYY-MM-DD格式输入日期")

def test_get_date(monkeypatch, capfd):
    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))

    # 无效输入测试
    with pytest.raises(ValueError):
        get_date("日期(YYYY-MM-DD):")
        captured = capfd.readouterr()
        assert "请以YYYY-MM-DD格式输入日期" in captured.out

    # 有效输入测试
    assert get_date("日期(YYYY-MM-DD):") == date(2023, 6, 12)
capfd.readouterr()允许你捕获本应打印到控制台的输出
如果你想了解更多请参考[文档][1]

希望这对你有帮助

[1]: https://docs.pytest.org/en/6.2.x/capture.html
英文:

By adding raise, you re-raise the ValueError after printing the error msg and thus it crashes.

so if you want the program to repromt for the user to re-enter data remove the 'raise' statement in the get-date function.

Hope This Helps

Here is the whole code:

import datetime
import pytest

def get_date(string):
    while True:
        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")

def test_get_date(monkeypatch, capfd):
    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))

    # invalid inputs test
    with pytest.raises(ValueError):
        get_date("Date (YYYY-MM-DD): ")
        captured = capfd.readouterr()
        assert "Please enter a date in the YYYY-MM-DD format" in captured.out

    # valid input test
    assert get_date("Date (YYYY-MM-DD): ") == date(2023, 6, 12)

capfd.readouterr() allows you to capture the output that would have been printed to the console.
if you wanna more about it doc

Hope This Is Helpful

答案2

得分: 1

以下是已翻译的代码部分:

这里有一种方法它在 get_input 函数内封装了在输入错误的情况下重新提示用户的操作

def get_input(string):
    date_str = input(string)
    while True:
        try:
            date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        except ValueError:
            print("请以 YYYY-MM-DD 格式输入日期")
            date_str = input(string)
    return date
英文:

Here's an approach that encapsulates the re-prompting the user in case of an input error inside the get_input function.

def get_input(string):
    date_str = input(string)
    while True:
        try:
            date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        except ValueError:
            print("Please enter a date in the YYYY-MM-DD format")
            date_str = input(string)
    return date

答案3

得分: 0

不需要将函数更改为布尔值。只需将日期字符串作为参数传递,并将用户输入移到单独的函数外部。

没有用户输入的get_date函数:

def get_date(date_str):
    try:
        date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        return date
    except ValueError:
        raise

测试:

def test_get_date():
    assert get_date("2023-06-12") == datetime.date(2023, 6, 12)

    with pytest.raises(ValueError):
        get_date("stuff")

    with pytest.raises(ValueError):
        get_date("2023/06/12")

    with pytest.raises(ValueError):
        get_date("2023/69/420")

在调用get_date之前,将用户输入添加回代码中,并将其作为date_str参数传递。

英文:

You don't need to change the function to a boolean. Instead just take the date string as an argument and move the user input outside to a separate function.

get_date function without user input:

def get_date(date_str):
    try:
        date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        return date
    except ValueError:
        raise

Testing:

def test_get_date():
    assert get_date("2023-06-12") == datetime.date(2023, 6, 12)

    with pytest.raises(ValueError):
        get_date("stuff")

    with pytest.raises(ValueError):
        get_date("2023/06/12")

    with pytest.raises(ValueError):
        get_date("2023/69/420")

Add the user input back into your code before you call get_date and hand it off as as the data_str argument.

答案4

得分: 0

你在原始代码中的思路是正确的 - 你只需要跳过对所有中间情况调用 get_date(),因为 get_date() 的执行将处理那些不良输入,然后提示下一个输入。

然后,在最后,我们可以断言输入列表已耗尽。

#script.py
import datetime

def get_date(string):
    # 使用循环而不是递归
    while True:
        try:
            date_str = input(string)
            date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        except ValueError:
            print("请以 YYYY-MM-DD 格式输入日期")

    return date
#test_script.py
import pytest
import datetime
from script import get_date

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))

    # 测试所有其他输入是否都被处理,并接受最后一个输入
    assert get_date("日期 (YYYY-MM-DD): ") == datetime.date(2023, 6, 12)

    with pytest.raises(StopIteration):
        _ = input("验证输入列表是否已耗尽")

如果输入列表未耗尽(在“接受”输入后还有值),则 Pytest 会失败,显示:

FAILED test_script.py::test_get_date - Failed: DID NOT RAISE <class 'StopIteration'>
英文:

You have the correct idea in your original code - you just need to skip calling get_date for all the intermediate cases, because the execution of get_date() will handle those bad inputs, and then prompt for the next input.

Then at the end we can assert that the list of inputs was exhausted.

#script.py
import datetime

def get_date(string):
    # Use a loop, not recursion
    while True:
        try:
            date_str = input(string)
            date = datetime.datetime.strptime(date_str, &quot;%Y-%m-%d&quot;).date()
        except ValueError:
            print(&quot;Please enter a date in the YYYY-MM-DD format&quot;)
    
    return date
#test_script.py
import pytest
import datetime
from script import get_date

def test_get_date(monkeypatch):
    inputs = [
        &quot;12/06/2023&quot;,
        &quot;12-06-2023&quot;,
        &quot;2023/06/12&quot;,
        &quot;2023/17/87&quot;,
        &quot;hello&quot;,
        &quot;2023-06-12&quot;,
    ]
    input_values = iter(inputs)
    monkeypatch.setattr(&quot;builtins.input&quot;, lambda _: next(input_values))

    # Test that all other inputs were handled and we accept the last one
    assert get_date(&quot;Date (YYYY-MM-DD): &quot;) == datetime.date(2023, 6, 12)

    with pytest.raises(StopIteration):
        _ = input(&quot;Verify inputs list is exhausted&quot;)

If the inputs list is not exhausted (there are values after an "accepted" input), then Pytest will fail with

FAILED test_script.py::test_get_date - Failed: DID NOT RAISE &lt;class &#39;StopIteration&#39;&gt;

huangapple
  • 本文由 发表于 2023年6月14日 23:28:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76475217.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定