Reputation: 41
I have the following function that I'd like to test:
# Ask user input, returns year
def what_year():
# Get the first input from the user.
year = input('\n\tEnter the year you would like to START your range: \n\t')
# Catch the error if the user doesn't enter a convertable string.
try:
year = int(year)
except ValueError:
print(error_msg.format(year))
what_year()
# We only want years between 2005 and today's year.
if year not in range(2005, int(datetime.now().year +1)):
print(error_msg.format(year))
what_year()
return year
I'd like to test it without having to raise any errors, since ideally the function would continually loop without raising an error until the user inputs an acceptable input.
I'm lost as to how to get pytest to loop through inputs. I'm tried to patch builtins.input with mock, and that works to give a specified input to my function, but ideally the test would be able to successfully loop through a list of inputs.
For example, from my test code below, in the real world a user would go through all of the invalid options first and then finally start putting in valid options and the function would finally start returning "year".
I roughly got around this by make a debug parameter for each of my functions, then raising a value error if debug is on for testing purposes, but this seems rudimentary:
# Ask user input, returns year
def what_year(debug=False):
# Get the first input from the user.
year = input('\n\tEnter the year you would like to START your range: \n\t')
# Catch the error if the user doesn't enter a convertable string.
try:
year = int(year)
except ValueError:
# Only raise ValueError if debug is on for testing purposes.
if debug:
raise ValueError
print(error_msg.format(year))
what_year(debug)
# We only want years between 2005 and today's year.
if year not in range(2005, int(datetime.now().year +1)):
if debug:
raise ValueError
print(error_msg.format(year))
what_year(debug)
return year
import mock
import pytest
from redditimagescraper import RedditImageScraper
@pytest.mark.parametrize('invalid_years', ["9999", "0", "", " ", "-2015"])
def test_what_year_invalid(invalid_years):
# Test invalid years
with pytest.raises(ValueError):
with mock.patch('builtins.input', return_value=invalid_years):
RedditImageScraper.what_year(True)
@pytest.mark.parametrize('valid_years', [str(year) for year in range(2005,2018)])
def test_what_year_valid(valid_years):
# Test valid years
with mock.patch('builtins.input', return_value=valid_years):
assert RedditImageScraper.what_year(True) == int(valid_years)
Any ideas how to rewrite this function or test function to more easily test inputs?
Upvotes: 3
Views: 2086
Reputation: 41
Not sure why but Barmar's comment on my question got my noggin joggin.
Anyway, here's an answer for those who have viewed the question. Per Barmar's comment I also updated my function.
# Ask user input, returns year
def what_year(start_or_end):
while True:
try:
# Catch the error if the user doesn't enter a convertable string.
# Get input from the user.
year = int(input('\n\tEnter the year you would like to {} your range: '.format(start_or_end)))
# We only want years between 2005 and today's year.
if year in range(2005, int(datetime.now().year + 1)):
# Success!
break
else:
print(error_msg.format(year))
except:
print(error_msg.format('ValueError'))
pass
return year
# Parametrize my valid answers
@pytest.mark.parametrize('valid_year', [str(year) for year in range(2005, datetime.now().year + 1)])
def test_what_year(valid_year):
# years = a bunch of invalid inputs with a valid input at the end
years = ["9999", "0", "", " ", "-2015", "-30", str(10^1000), valid_year]
# side_effect, when given an iterable, iterates through
# each time the patched function is called (in this case input())
with mock.patch('builtins.input', side_effect=years):
assert RedditImageScraper.what_year('start') == int(valid_year)
running pytest -v gives me this:
============================= test session starts ==============================
platform linux -- Python 3.6.0, pytest-3.0.7, py-1.4.33, pluggy-0.4.0 -- /home/ardeaf/Projects/RedditImageScraper/venv/bin/python3
cachedir: .cache
rootdir: /home/ardeaf/Projects/RedditImageScraper, inifile: setup.cfg
collecting ... collected 12 items
tests/RedditImageScraper_test.py::test_what_year[2005] PASSED
tests/RedditImageScraper_test.py::test_what_year[2006] PASSED
tests/RedditImageScraper_test.py::test_what_year[2007] PASSED
tests/RedditImageScraper_test.py::test_what_year[2008] PASSED
tests/RedditImageScraper_test.py::test_what_year[2009] PASSED
tests/RedditImageScraper_test.py::test_what_year[2010] PASSED
tests/RedditImageScraper_test.py::test_what_year[2011] PASSED
tests/RedditImageScraper_test.py::test_what_year[2012] PASSED
tests/RedditImageScraper_test.py::test_what_year[2013] PASSED
tests/RedditImageScraper_test.py::test_what_year[2014] PASSED
tests/RedditImageScraper_test.py::test_what_year[2015] PASSED
tests/RedditImageScraper_test.py::test_what_year[2016] PASSED
tests/RedditImageScraper_test.py::test_what_year[2017] PASSED
========================== 12 passed in 0.31 seconds ===========================
Upvotes: 1