对接收和显示用户输入的函数进行文档测试 - Python(让我抓狂)
我现在正在用Python(3.1)写一个小应用程序,像个乖孩子一样,我在边写边做测试。不过,我遇到了一个方法,无法进行测试。这个方法里有一个input()
,因此我不太确定在测试的“期望”部分应该放什么。
下面是一个示例代码,来说明我的问题:
"""
>>> getFiveNums()
Howdy. Please enter five numbers, hit <enter> after each one
Please type in a number:
Please type in a number:
Please type in a number:
Please type in a number:
Please type in a number:
"""
import doctest
numbers = list()
# stores 5 user-entered numbers (strings, for now) in a list
def getFiveNums():
print("Howdy. Please enter five numbers, hit <enter> after each one")
for i in range(5):
newNum = input("Please type in a number:")
numbers.append(newNum)
print("Here are your numbers: ", numbers)
if __name__ == "__main__":
doctest.testmod(verbose=True)
当我运行测试时,程序在打印“期望”部分后就立刻停止执行,接着让我一个接一个地输入五个数字(没有提示),然后才继续执行。如下所示:
我不知道在我的测试的期望部分可以放什么,才能测试一个接收并显示用户输入的方法。所以我最后的问题是,这个函数可以进行测试吗?
7 个回答
我找到了一种不同的方法。
"""
>>> get_five_nums(testing=True)
Howdy. Please enter five numbers, hit <enter> after each one.
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Please type in a number: 1
Here is a list of the numbers you entered: [1, 1, 1, 1, 1]
>>>
"""
import doctest
numbers = []
def get_five_nums(testing=False):
"""Stores 5 user-entered numbers (strings, for now) in a list."""
print("Howdy. Please enter five numbers, hit <enter> after each one.")
for i in range(5):
new_num = int(input("Please type in a number: "))
if testing:
print(new_num)
numbers.append(new_num)
print("Here is a list of the numbers you entered: ", numbers)
if __name__ == "__main__":
doctest.testmod(verbose=True)
把上面的代码保存到一个叫foo.py的文件里。然后再创建一个叫input.txt的文件。
这个文件里只需要写:
1
1
1
1
1
五个数字1,每个数字占一行。
要测试你的程序,可以在终端或命令提示符下这样做(我用的是Mac):
$ python foo.py < input.txt
这个方法可以很方便地用于任何程序的用户输入。这样你就可以复制终端的输出,并把它用作你的测试文档。
注意:在终端调用的函数是get_five_nums()。而在你的测试文档里需要写成get_five_nums(testing=True)。
虽然doctest看起来并不是为了这样使用,但这仍然是一个很实用的小技巧。
让这个测试变得简单的方法就是使用参数注入:
def getFiveNums(input_func=input):
print("Howdy. Please enter five numbers, hit <enter> after each one")
for i in range(5):
newNum = input_func("Please type in a number:")
numbers.append(newNum)
print("Here are your numbers: ", numbers)
实际上,你不能指望像这样对输入/输出进行单元测试——你不需要担心调用input
可能会失败。最好的办法是传入一个模拟方法;类似于
def fake_input(str):
print(str)
return 3
这样在你的文档测试中,你实际上是在测试getFiveNums(fake_input)
。
而且,通过现在就打破对input
的直接依赖,如果你以后要把这段代码移植到其他地方,而那个地方又不使用命令行,你只需替换成新的获取输入的代码(无论是图形界面应用中的对话框,还是网页应用中的Javascript弹窗等等)。
我知道你在问关于doctest的答案,但我想说,这种类型的函数可能不太适合用doctest来测试。我自己用doctest更多是为了写文档,而不是单纯的测试。在我看来,用doctest来做这个可能不会是个好文档。
如果用unittest的方法,可能会像这样:
import unittest
# stores 5 user-entered numbers (strings, for now) in a list
def getFiveNums():
numbers = []
print "Howdy. Please enter five numbers, hit <enter> after each one"
for i in range(5):
newNum = input("Please type in a number:")
numbers.append(newNum)
return numbers
def mock_input(dummy_prompt):
return 1
class TestGetFiveNums(unittest.TestCase):
def setUp(self):
self.saved_input = __builtins__.input
__builtins__.input = mock_input
def tearDown(self):
__builtins__.input = self.saved_input
def testGetFiveNums(self):
printed_lines = getFiveNums()
self.assertEquals(printed_lines, [1, 1, 1, 1, 1])
if __name__ == "__main__":
unittest.main()
这可能并不是在严格测试你提出的函数,但你能明白我的意思。