2017-03-29 83 views
2

,我有以下的功能,我想测试:测试多次调用输入()

# 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 

我想测试它,而无需提出任何错误,因为理想的功能会不断循环,直到用户输入可接受的输入为止。

我失去了如何让pytest循环输入。我尝试用mock修补builtins.input,并且为我的函数提供指定的输入,但理想情况下,测试将能够成功循环输入列表。

例如,从我的测试代码如下,在真实世界中,用户会首先查看所有无效选项,然后开始输入有效选项,并且函数最终会开始返回“年”。

我大概得到了解决此通过让我的每个功能,然后养值误差调试参数,如果调试是出于测试目的,但这似乎简陋:

功能:

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

任何想法如何重写这个功能或测试功能更方便地测试投入?

+0

不使用递归在Python中循环,使用'while'循环。但是如果你使用递归,你必须返回递归调用返回的内容:'return what_year(debug)' – Barmar

+0

如果我将它改为while循环,Pytest会在每个输入调用中循环参数吗?或者我应该将return_value更改为side_effect,将参数化信息丢弃,并将其列入无效且有效的年份列表? – Ardeezy

+0

对不起,我对pytest一无所知。但你不应该改变你的代码设计来匹配单元测试框架。 – Barmar

回答

0

不知道为什么,但巴尔玛对我的问题的评论得到了我的头衔joggin。

无论如何,对于那些查看过这个问题的人来说,这是一个答案。 Per Barmar的评论我也更新了我的功能。

what_year()函数:

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

运行pytest -v使我这个:

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