简体   繁体   English

测试需要用户 input() 的 Python 函数

[英]Testing Python functions that require user input()

I want to test the following function:我想测试以下功能:

def get_inputs():
    str_from_user = input()
    list_of_marks = []
    while str_from_user != "exit":
        list_of_marks.append('!')
        str_from_user = input()
    return list_of_marks

is there a way to test multiple scenarios?有没有办法测试多个场景? something like this (but only that works):像这样的东西(但只有这样有效):

def test_get_inputs():
    assert get_inputs(){"hey", "coco", "milo", "exit"} == "!!!"
    assert get_inputs(){"exit"} == ""
    assert get_inputs(){"horses have long faces", "exit} == "!"

There is no obvious way to test your function as it is currently written, because you are reading from standard input.没有明显的方法来测试当前编写的函数,因为您是从标准输入读取的。 This is a side effect that makes your function non- pure and hard to test.这是一个副作用,使您的函数不纯且难以测试。 In other words, the input comes from outside your program, so you do not have control over it.换句话说,输入来自您的程序外部,因此您无法控制它。

You should rewrite your function in a way that makes it easy to provide input and check output within the program.您应该以一种易于程序中提供输入和检查输出的方式重写您的函数。 You can do this passing a function to use as a way to get input instead of always using input() .您可以通过传递一个函数来执行此操作,以用作获取输入的一种方式,而不是始终使用input() Then, for testing, you can create a simple iterator object over a list of known inputs and pass its __next__ method as input function.然后,为了测试,您可以在已知输入列表上创建一个简单的迭代器对象,并将其__next__方法作为输入函数传递。

Here's an example of how this could be done:这是一个如何做到这一点的示例:

def get_inputs(input_function):
    str_from_user = input_function()
    list_of_marks = []
    while str_from_user != "exit":
        list_of_marks.append('!')
        str_from_user = input_function()
    return list_of_marks

def test_get_inputs():
    input_func_one   = iter(["hey", "coco", "milo", "exit"]).__next__
    input_func_two   = iter(["exit"]).__next__
    input_func_three = iter(["horses have long faces", "exit"]).__next__
    assert get_inputs(input_func_one) == ['!', '!', '!']
    assert get_inputs(input_func_two) == []
    assert get_inputs(input_func_three) == ['!']

Another option, as suggested by @chepner in the above comments, would be to pass the function a file-like object, which can be easily mocked using the io module with either BytesIO or StringIO .正如@chepner在上述评论中所建议的那样,另一个选项是向函数传递一个类似文件的对象,可以使用带有BytesIOStringIOio模块轻松模拟该对象。 Take care in this case though because input() strips the trailing newline, but StringIO.readline() does not.在这种情况下要小心,因为input()会去除尾随的换行符,但StringIO.readline()不会。

from io import StringIO

def get_inputs(input_file):
    str_from_user = input_file.readline().rstrip()
    list_of_marks = []
    while str_from_user != "exit":
        list_of_marks.append('!')
        str_from_user = input_file.readline().rstrip()
    return list_of_marks

def test_get_inputs():
    assert get_inputs(StringIO("hey\ncoco\nmilo\nexit\n")) == ['!', '!', '!']
    assert get_inputs(StringIO("exit\n")) == []
    assert get_inputs(StringIO("horses have long faces\nexit\n")) == ['!']

You could also adapt the first case (passing an input function) to work with StringIO by simply passing StringIO("...").readline as function.您还可以通过简单地将StringIO("...").readline作为函数传递来调整第一种情况(传递输入函数)以使用StringIO


Not what you have a function that is easily testable, you can write a simple wrapper that uses the function and works with standard input instead:不是你有一个易于测试的函数,你可以编写一个使用该函数并使用标准输入的简单包装器:

# If using an input function
def get_inputs_from_stdin():
    return get_inputs(input)

# If using a file-like object
import sys

def get_inputs_from_stdin():
    return get_inputs(sys.stdin)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM