2011-02-01 42 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