假設我有這樣的Python代碼原始輸入:蟒蛇嘲諷的單元測試
def answer():
ans = raw_input('enter yes or no')
if ans == 'yes':
print 'you entered yes'
if ans == 'no':
print 'you entered no'
我如何寫這個單元測試?我知道我必須使用'模擬',但我不明白如何。任何人都可以舉一個簡單的例子嗎
假設我有這樣的Python代碼原始輸入:蟒蛇嘲諷的單元測試
def answer():
ans = raw_input('enter yes or no')
if ans == 'yes':
print 'you entered yes'
if ans == 'no':
print 'you entered no'
我如何寫這個單元測試?我知道我必須使用'模擬',但我不明白如何。任何人都可以舉一個簡單的例子嗎
您無法修補輸入,但可以將其包裝爲使用mock.patch()。這裏是一個解決方案:
from unittest.mock import patch
from unittest import TestCase
def get_input(text):
return input(text)
def answer():
ans = get_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
class Test(TestCase):
# get_input will return 'yes' during this test
@patch('yourmodule.get_input', return_value='yes')
def test_answer_yes(self, input):
self.assertEqual(answer(), 'you entered yes')
@patch('yourmodule.get_input', return_value='no')
def test_answer_no(self, input):
self.assertEqual(answer(), 'you entered no')
請記住,這個片段將只在Python版本3.3+
def answer():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
def test_answer_yes():
assert(answer() == 'you entered yes')
def test_answer_no():
assert(answer() == 'you entered no')
origin_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda x: "yes"
test_answer_yes()
__builtins__.raw_input = lambda x: "no"
test_answer_no()
__builtins__.raw_input = origin_raw_input
好工作,首先,我覺得有必要指出的是,在有問題的原代碼,實際上有兩件事需要解決:
raw_input
(輸入副作用)需要被模擬。print
(輸出副作用)需要檢查。在單元測試的理想功能中,不會有副作用。一個函數可以簡單地通過傳遞參數來測試,並且它的輸出將被檢查。但是,我們經常想要測試那些功能並不理想的IE,像你這樣的功能。
那麼我們該怎麼做?那麼,在Python 3.3中,我上面列出的兩個問題變得微不足道,因爲unittest
模塊獲得了模擬和檢查副作用的能力。但是,截至2014年初,只有30%的Python程序員已經轉向3.x,所以爲了其他70%的Python程序員仍然使用2.x,我將概述一個答案。以目前的速度,3.x直到2019年纔會超過2.x,而2.x直到2027年纔會消失。所以我認爲這個答案在未來幾年內將會有用。
我想一次解決上面列出的問題,因此我將首先將使用print
作爲其輸出的函數更改爲使用return
。沒有意外,下面是代碼:
def answerReturn():
ans = raw_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'
所以我們需要做的是模擬raw_input
。足夠簡單 - Omid Raha's answer to this very question向我們展示瞭如何通過使用我們的模擬實現來調試__builtins__.raw_input
實現。除了他的回答沒有正確地組織成一個TestCase
和功能,所以我會證明這一點。
import unittest
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'yes'
self.assertEqual(answerReturn(), 'you entered yes')
__builtins__.raw_input = original_raw_input
def testNo(self):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: 'no'
self.assertEqual(answerReturn(), 'you entered no')
__builtins__.raw_input = original_raw_input
小記只是關於Python命名約定 - 這是由解析器必需的,但不使用通常命名_
變量,在lambda未使用的變量的情況下(通常是向用戶顯示在提示在這種情況下,你想知道爲什麼它是必需的),這是raw_input
的情況。
無論如何,這是混亂和多餘的。所以我將通過添加contextmanager
來消除重複,這將允許簡單的with
陳述。
from contextlib import contextmanager
@contextmanager
def mockRawInput(mock):
original_raw_input = __builtins__.raw_input
__builtins__.raw_input = lambda _: mock
yield
__builtins__.raw_input = original_raw_input
class TestAnswerReturn(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'):
self.assertEqual(answerReturn(), 'you entered yes')
def testNo(self):
with mockRawInput('no'):
self.assertEqual(answerReturn(), 'you entered no')
我認爲這很好地回答了第一部分。在第二部分 - 檢查print
。我發現這更棘手 - 我很想聽聽有沒有人有更好的答案。
不管怎麼說,在print
聲明不能被覆蓋,但如果你使用print()
功能,而不是(你應該)和from __future__ import print_function
,你可以使用以下命令:
class PromiseString(str):
def set(self, newString):
self.innerString = newString
def __eq__(self, other):
return self.innerString == other
@contextmanager
def getPrint():
promise = PromiseString()
original_print = __builtin__.print
__builtin__.print = lambda message: promise.set(message)
yield promise
__builtin__.print = original_print
class TestAnswer(unittest.TestCase):
def testYes(self):
with mockRawInput('yes'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered yes')
def testNo(self):
with mockRawInput('no'), getPrint() as response:
answer()
self.assertEqual(response, 'you entered no')
這裏棘手的一點是,你需要到yield
迴應with
塊之前的迴應。但是,在調用with
塊中的print()
之前,您無法知道該響應的結果。如果字符串是可變的,這將很好,但它們不是。所以取而代之的是一個小承諾或代理類 - PromiseString
。它只做兩件事 - 允許設置一個字符串(或者其他任何東西),讓我們知道它是否等於不同的字符串。 A PromiseString
爲yield
ed,然後設置爲with
塊內通常爲print
的值。
希望你能感謝我寫下來的所有詭計,因爲我花了大約90分鐘的時間把這個晚上放在一起。我測試了所有這些代碼,並驗證了它與Python 2.7一起工作。
剛跑過同樣的問題,但我只是嘲笑出__builtin__.raw_input
。
只在Python 2上測試過。pip install mock
如果您還沒有安裝該軟件包。
from mock import patch
from unittest import TestCase
class TestAnswer(TestCase):
def test_yes(self):
with patch('__builtin__.raw_input', return_value='yes') as _raw_input:
self.assertEqual(answer(), 'you entered yes')
_raw_input.assert_called_once_with('enter yes or no')
def test_no(self):
with patch('__builtin__.raw_input', return_value='no') as _raw_input:
self.assertEqual(answer(), 'you entered no')
_raw_input.assert_called_once_with('enter yes or no')
另外,使用該庫,可以簡化兩項測試:
from genty import genty, genty_dataset
from mock import patch
from unittest import TestCase
@genty
class TestAnswer(TestCase):
@genty_dataset(
('yes', 'you entered yes'),
('no', 'you entered no'),
)
def test_answer(self, expected_input, expected_answer):
with patch('__builtin__.raw_input', return_value=expected_input) as _raw_input:
self.assertEqual(answer(), expected_answer)
_raw_input.assert_called_once_with('enter yes or no')
,不得不上述適應的答案。我的解決方案將通用代碼納入自定義runTest
方法中,並向您展示如何修補input()
和print()
。下面是運行的代碼: 進口單元測試 從IO導入StringIO的 從unittest.mock進口貼片
def answer():
ans = input('enter yes or no')
if ans == 'yes':
print('you entered yes')
if ans == 'no':
print('you entered no')
class MyTestCase(unittest.TestCase):
def runTest(self, given_answer, expected_out):
with patch('builtins.input', return_value=given_answer), patch('sys.stdout', new=StringIO()) as fake_out:
answer()
self.assertEqual(fake_out.getvalue().strip(), expected_out)
def testNo(self):
self.runTest('no', 'you entered no')
def testYes(self):
self.runTest('yes', 'you entered yes')
if __name__ == '__main__':
unittest.main()
的[電源輸入到Python單元測試(
我用'nose2'(不直接使用'unittest')調整你的答案,這對我很好。需要注意的一點是,如果將'fakeout.getvalue()'更改爲'fakeout.getvalue()。strip()',則可以避免傳遞額外的換行符。 –
可能重複http://stackoverflow.com/questions/2617057/supply -inputs-to-python-unittests) – jonrsharpe
我找不到答案 – user3156971
三個答案之一*字面意思是*關於使用'mock'來測試'raw_input' – jonrsharpe