亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

python模擬單元測試中的原始輸入

python模擬單元測試中的原始輸入

GCT1015 2019-10-11 10:06:00
假設我有以下python代碼:def answer():    ans = raw_input('enter yes or no')    if ans == 'yes':        print 'you entered yes'    if ans == 'no':        print 'you entered no'我該如何為此編寫單元測試?我知道我必須使用“模擬”,但我不知道如何使用。有人可以舉一個簡單的例子嗎?
查看完整描述

3 回答

?
幕布斯7119047

TA貢獻1794條經驗 獲得超8個贊

您不能修補輸入,但是可以包裝它以使用模擬.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+


查看完整回答
反對 回復 2019-10-11
?
猛跑小豬

TA貢獻1858條經驗 獲得超8個贊

好的,首先,我覺得有必要指出,在原始代碼中,實際上需要解決兩個問題:


raw_input (輸入副作用)需要被嘲笑。

print (輸出副作用)需要檢查。

在理想的單元測試功能中,不會有副作用。簡單地通過傳遞參數來測試函數,然后檢查其輸出。但是通常我們想在像您這樣的函數中測試不理想的函數IE。


那么我們該怎么辦呢?好吧,在Python 3.3中,我上面列出的兩個問題變得微不足道,因為該unittest模塊獲得了模擬和檢查副作用的功能。但是,從2014年初開始,只有30%的Python程序員開始使用3.x,因此,為了使另外70%的Python程序員仍在使用2.x,我將概述一個答案。以目前的速度,直到2019年,3.x才會超過2.x,直到2027年,2.x才會消失。因此,我認為這個答案將在未來幾年中有用。


我想一次解決上面列出的問題,因此我將首先將您的功能從使用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對這個問題的回答向我們展示了如何通過__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塊之前做出響應。但是,直到調用塊print()內部,您才能知道該響應是什么with。如果字符串是可變的,那會很好,但不是。因此,做了一個小的Promise或proxy類PromiseString。它僅做兩件事-允許設置一個字符串(或其他任何東西),并讓我們知道它是否等于另一個字符串。阿PromiseString是yieldED,然后設置為通常將值print的內with塊。


希望您能欣賞我今晚花了大約90分鐘時間整理而成的所有技巧。我測試了所有這些代碼,并驗證了它們在Python 2.7中都可以使用。


查看完整回答
反對 回復 2019-10-11
?
胡說叔叔

TA貢獻1804條經驗 獲得超8個贊

我正在使用Python 3.4,不得不調整以上答案。我的解決方案將通用代碼排除在自定義runTest方法之外,并向您展示如何修補input()和print()。這是運行的代碼:從io導入unittest從unittest.mock導入補丁導入StringIO


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


查看完整回答
反對 回復 2019-10-11
  • 3 回答
  • 0 關注
  • 749 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號