2011-02-01 40 views
5

我对使用Python一开始编程类一年级的学生。我的python-fu自己并不是很强大,但我想尝试自动化一些评分。如何测试使用input()(可能与unittest?)的初学者学生Python程序?

网上看,我喜欢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

两条输入行缺少右括号。 – Javier 2011-02-28 14:54:55

+0

@Javier:固定。谢谢,有人编辑了我的问题,并添加了`eval(`但没有关闭另一边。 – Jason 2011-03-05 20:12:22

回答

8

编辑:仅提出这一点,因为该示例不是单元测试-能够(和我假设初学的学生将只是约束)

如果你只是关注输出匹配你在找什么,为什么不使用一些“愚蠢”的bash混淆?喜欢的东西:

echo -e "2\n3" | python test.py | grep -q "The sum is 5" && echo "Success" 

如果你正在做这样的比较琐碎的程序,那么这应该是足够的,或不够好,解决方案,它需要一些努力。

+0

这实际上对我非常有用。你是对的,制作一个可测试的程序不在学生的教导范围之内(这是一个很好的入门级),但这样的事情实际上是最好的。 – Jason 2011-02-01 19:41:42

4

你不能真正单元测试。关于编写单元测试的一件事情是你经常需要写你的代码,让它被单元测试。因此,在这种情况下,您需要将调用分解为一个单独的函数,然后您可以进行修补。

def my_input(prompt): 
    return input(prompt) 

def main(): 
    a = int(eval(my_input("Enter an integer: ")) 

等现在你的测试可以猴子补丁myscript.my_input返回你想要的值。

2

如果您需要与命令行程序的更复杂的交互,可以使用echo,那么您可能需要查看expect

2

docs

目的sys.stdin,sys.stdout的和 sys.stderr都是初始化到文件对应于 解释器的标准输入,输出 和错误流 对象。

所以按照这样的逻辑,这样的事情似乎工作。您需要输入创建一个文件:

$ 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 
2

简短的回答,不这样做。您必须设计可测试性。这意味着提供一种简单的方式来提供接口供事物用来与系统资源交谈,以便您可以在测试时提供这些接口的替代实现。

在其他答案中描述的猴子补丁解决方案确实有效,但它是最原始的选项。就我个人而言,我会为用户交互编写一个接口类。例如:

class UserInteraction(object): 
    def get_input(self): 
     raise NotImplementedError() 
    def send_output(self, output): 
     raise NotImplementedError() 

然后事情需要交谈的用户可以得到你的类的实例作为构造函数或函数参数。默认实现可以调用实际的input函数或其他任何函数,但是有一个版本用于测试,它提供样本输入或缓冲输出以便可以检查。

顺便说一句,这就是为什么我讨厌Singleton(它无法真正在Python中有效实现)。它通过创建一个全局可访问的实例来破坏你的测试能力,这个实例不能被stub版本删掉以供测试。

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 
0

将sys.stdin替换为StringIO(或cStringIO)对象。

2

我的建议是使用Python提供了单元测试的两个框架的一个重构代码:单元测试(又名PyUnit中)和文档测试

这是使用一个例子单元测试

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

而这是使用文档测试一个例子:

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

随着文档测试我使用输入由另一个例子中()(我假设你正在使用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