英文:
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, "%Y-%m-%d").date()
except ValueError:
print("Please enter a date in the YYYY-MM-DD format")
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))
# Test that all other inputs were handled and we accept the last one
assert get_date("Date (YYYY-MM-DD): ") == datetime.date(2023, 6, 12)
with pytest.raises(StopIteration):
_ = input("Verify inputs list is exhausted")
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 <class 'StopIteration'>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论