我是使用Python开始编程类的平地机。我的python-fu本身并不那么强大,但我想尝试自动化一些评分。
在网上看,我喜欢PyUnit测试套件,虽然它可能对我想要的东西有点过分。
我的问题是我不确定如何将我想要的测试输入传递给学生的函数,因为他们还没有使用命令行参数甚至多个函数,而是通过input()
获取用户输入功能
一个愚蠢的例子:
#/usr/bin/python3.1
# A silly example python program
def main():
a = int(input("Enter an integer: "))
b = int(input("Enter another integer: "))
c = a+b
print("The sum is %d" % c)
if __name__ == '__main__'
main()
对于我的愚蠢示例,我如何编写可以检查几个不同输入的输出的单元测试? (即,如果我将2和3传递给输入,则输出字符串应为“总和为5”)
答案 0 :(得分:8)
编辑:只提出这个,因为这个例子不能单元测试(而且我假设初学者会被约束困惑)
如果你只关心输出匹配你想要的东西,为什么不只是使用一些“愚蠢的”bash?类似的东西:
echo -e "2\n3" | python test.py | grep -q "The sum is 5" && echo "Success"
如果你正在做这样相对琐碎的程序,那么这应该是一个足够的或足够好的解决方案,只需要很少的努力。
答案 1 :(得分:4)
你无法真正对其进行单元测试。编写单元测试的一个原因是您经常需要以不同的方式编写代码以允许它进行单元测试。因此,在这种情况下,您需要将输入的调用分解为单独的函数,然后可以对其进行修补。
def my_input(prompt):
return input(prompt)
def main():
a = int(eval(my_input("Enter an integer: "))
等。现在,您的测试可以修补myscript.my_input
以返回您想要的值。
答案 2 :(得分:2)
如果您需要与echo
提供的命令行程序进行更复杂的交互,那么您可能需要查看expect
。
答案 3 :(得分:2)
来自docs:
对象sys.stdin,sys.stdout和 sys.stderr初始化为file 对应的对象 口译员的标准输入,输出 和错误流。
所以遵循这个逻辑,这样的事情似乎有效。使用所需输入创建一个文件:
$ cat sample_stdin.txt
hello
world
然后重定向sys.stdin
以指向该文件:
#!/usr/bin/env python
import sys
fh = open('sample_stdin.txt', 'r')
sys.stdin = fh
line1 = raw_input('foo: ')
line2 = raw_input('bar: ')
print line1
print line2
输出:
$python redirecting_stdin.py
foo: bar: hello
world
答案 4 :(得分:2)
简短回答,不要这样做。您必须设计可测试性。这意味着提供一种简单的方法来提供用于与系统资源通信的东西的接口,以便在测试时提供这些接口的替代实现。
另一个答案中描述的猴子补丁解决方案确实有效,但它是您最明智的选择。就个人而言,我会为用户交互编写一个接口类。例如:
class UserInteraction(object):
def get_input(self):
raise NotImplementedError()
def send_output(self, output):
raise NotImplementedError()
然后需要与用户交谈的事情可以将您的类的实例作为构造函数或函数参数。默认实现可以调用实际的input
函数或其他任何函数,但是有一个用于测试的版本提供了样本输入或缓冲输出,因此可以检查它。
顺便说一句,这就是为什么我讨厌Singleton(无论如何都无法在Python中真正有效地实现)。它通过创建一个全局可访问的实例来破坏您的测试能力,该实例无法通过存根版本进行测试。
答案 5 :(得分:2)
我的建议是使用Python为单元测试提供的两个框架之一重构代码: unittest (又名PyUnit)和 doctest 。
这是使用 unittest :
的示例import unittest
def adder(a, b):
"Return the sum of two numbers as int"
return int(a) + int(b)
class TestAdder(unittest.TestCase):
"Testing adder() with two int"
def test_adder_int(self):
self.assertEqual(adder(2,3), 5)
"Testing adder() with two float"
def test_adder_float(self):
self.assertEqual(adder(2.0, 3.0), 5)
"Testing adder() with two str - lucky case"
def test_adder_str_lucky(self):
self.assertEqual(adder('4', '1'), 5)
"Testing adder() with two str"
def test_adder_str(self):
self.assertRaises(ValueError, adder, 'x', 'y')
if __name__ == '__main__':
unittest.main()
这是使用 doctest :
的示例# adder.py
def main(a, b):
"""This program calculate the sum of two numbers.
It prints an int (see %d in print())
>>> main(2, 3)
The sum is 5
>>> main(3, 2)
The sum is 5
>>> main(2.0, 3)
The sum is 5
>>> main(2.0, 3.0)
The sum is 5
>>> main('2', '3')
Traceback (most recent call last):
...
TypeError: %d format: a number is required, not str
"""
c = a + b
print("The sum is %d" % c)
def _test():
import doctest, adder
return doctest.testmod(adder)
if __name__ == '__main__':
_test()
使用 doctest 我使用input()做了另一个例子(我假设你使用的是Python 3.X):
# adder_ugly.py
def main():
"""This program calculate the sum of two numbers.
It prints an int (see %d in print())
>>> main()
The sum is 5
"""
a = int(input("Enter an integer: "))
b = int(input("Enter another integer: "))
c = a+b
print("The sum is %d" % c)
def _test():
import doctest, adder_ugly
return doctest.testmod(adder_ugly)
if __name__ == '__main__':
_test()
我会使用-v
选项运行上述每个示例:
python adder_ugly.py -v
供您参考:
http://docs.python.org/py3k/library/unittest.html?highlight=unittest#unittest
和
http://docs.python.org/py3k/library/doctest.html#module-doctest
答案 6 :(得分:0)
您可以模拟input
函数来提供测试环境的输入。
这似乎可能有效。这是未经测试的。
class MockInput( object ):
def __init__( self, *values ):
self.values= list(values)
self.history= []
def __call__( self, *args, **kw ):
try:
response= self.values.pop(0)
self.history.append( (args, kw, response) )
return response
except IndexError:
raise EOFError()
class TestSomething( unittest.TestCase ):
def test_when_input_invalid( self ):
input= MockInput( "this", "and", "that" )
# some test case based on the input function
答案 7 :(得分:0)
将sys.stdin替换为StringIO(或cStringIO)对象。