簡體   English   中英

Python使用mock進行多用戶輸入

[英]Python using mock for a multiple user inputs

這個問題的后續內容。

我在for循環中接受用戶輸入並編寫了一個測試用例test_apple_record 在這個for循環中,它查詢一個方法self.dispatch_requested() (未顯示),它可以隨機返回True或False。 根據這個答案,代碼會要求用戶提供另一個輸入 - 應該調度托盤。

我現在用的是side_effect為了論證mock.patch 如何使用mock自動傳遞酒店編號作為用戶輸入? 我仍然希望繼續將數字[5, 6, 7]傳遞給for循環,但現在還要根據self.dispatch_requested()響應傳遞酒店編號

謝謝

class SomeClass(unittest.TestCase):
    def apple_counter(self):
        apple_record = {}

        for i in range(3):
            apple_tray = input("enter tray number:")
            apple_record[apple_tray]  =  (i+1)*10
            print("i=%d, apple_record=%s"%(i, apple_record))

            if self.dispath_requested():
                number = input("Enter Hotel number to dispatch this tray:")
                update_hotel_record(number, apple_tray)

    def update_hotel_record(self, number, tray):
        self.hotel_record[number] = tray

    def test_apple_record(self):
        with mock.patch('builtins.input', side_effect=[5, 6, 7]):
            self.apple_counter()

結果我的最后答案畢竟沒用! 由於無法知道您需要哪個輸入但是要讀取提示,您可以簡單地將input()函數替換為根據提示提供不同答案的函數。

# first we need a generator for each type of response to `input()`

def tray_number_generator():
    trays = ["1", "5", "7"]
    for i in trays:
        yield i

trays = tray_number_generator()

def room_number_generator():
    rooms = ["112", "543", "724"]
    for i in rooms:
        yield i

rooms = room_number_generator()

# this can be written simpler as a generator expression like this:

trays = (tray for tray in ["1", "5", "7"])
rooms = (room for room in ["112", "543", "724"])

# now you can write a function that selects the next output depending on the prompt:

def mock_input(prompt):
    if "room" in prompt.lower():
        return next(rooms)
    if "tray" in prompt.lower():
        return next(trays)

# this can now be used to replace the `input()` function

with mock.patch('builtins.input', mock_input):
    do_stuff()

你真的希望你的side_effect看起來像這樣:

m_input.side_effect = [1, 100, 2, 200, 3, 300]

每次調用輸入方法時,它都將返回下一個項目。 因此,每次在循環中,您都會調用兩次輸入。

另外,我不知道你的單元測試的最終結構,但是,看到你在循環中調用的第二個輸入周圍有一個條件語句,你應該設置一個圍繞該方法的模擬以始終返回True。

當你到達想要測試你的代碼的場景時,self.dispath_requested()返回false,你必須記住第二個輸入不會被調用,所以你的side_effect必須相應地重寫以匹配您的代碼的預期行為。

另外,最后,我不確定你的代碼實際上是什么樣的,但是,根據你在同一個類下的實際實現和測試代碼的方式,我強烈建議不要這樣做。 嘗試類似這樣的結構:

創建一個單獨的測試類:

class Tests(unittest.TestCase):
    def setUp(self):
        self.s = SomeClass()

    @patch('__builtin__.input')
    def test_apple_record(self, m_input):
        m_input.side_effect = [1, 100, 2, 200, 3, 300]
        self.s.apple_counter()


if __name__ == '__main__':
    unittest.main()

因此,您創建了一個SomeClass實例,然后實際上這將使您更容易模擬對象的屬性,這將使您的單元測試更容易編寫。

您還會注意到我使用了裝飾器(@patch)而不是“with”上下文。 這是個人偏好,我發現使用裝飾器閱讀代碼要容易得多。

希望這可以幫助。

我不想深入研究如何模擬inputdispatch_requested並將答案結合起來以獲得完整的控制並為此方法編寫一個良好的單元測試。 我認為如何更改設計以使測試(以及代碼)更簡單,更清晰更有趣:

class SomeClass(object):
    def apple_counter(self):
        apple_record = {}

        for i in range(3):
            apple_tray = input("enter tray number:")
            apple_record[apple_tray]  =  (i+1)*10
            print("i=%d, apple_record=%s"%(i, apple_record))
            self._dispatch_and_ask_number()

    def _dispatch_and_ask_number(self):
        if self.dispatch_requested():
            number = self._ask_hotel_number()
            update_hotel_record(number, apple_tray)

    def _ask_try_number(self):
        return input("enter tray number:")

    def _ask_hotel_number(self):
        return input("Enter Hotel number to dispatch this tray:")

    def update_hotel_record(self, number, tray):
        self.hotel_record[number] = tray

現在,您可以更好地創建一個新類,只需要一個用戶輸入的責任,然后模擬它以在測試中擁有完整的控件:

class AskUserInput(class):
    try_number_message = "Enter tray number:"
    hotel_number_message = "Enter Hotel number to dispatch this tray:"

    def try_number(self):
        return input(self.try_number_message)

    def hotel_number(self):
        return input(self.hotel_number_message)

SomeClass可以改為:

class SomeClass(object):

    _ask = AskUserInput()

    def apple_counter(self):
        apple_record = {}

        for i in range(3):
            apple_tray = self._ask.try_number()
            apple_record[apple_tray]  =  (i+1)*10
            print("i=%d, apple_record=%s"%(i, apple_record))
            self._dispatch_and_ask_number()

    def _dispatch_and_ask_number(self):
        if self.dispatch_requested():
            number = self._ask.hotel_number()
            update_hotel_record(number, apple_tray)

    def update_hotel_record(self, number, tray):
        self.hotel_record[number] = tray

最后是測試

class TestSomeClass(unittest.TestCase):
    @patch("AskUserInput.try_number")
    @patch("AskUserInput.hotel_number")
    def test_apple_record(self, mock_try_number, mock_hotel_number):
        # Now you can use both side_effects and return_value
        # to make your test clear and simple on what you test.

如果您正在使用遺留代碼,那么這個approch並不是真正有用,但是如果您正在測試您正在開發的內容,最好將其轉換為更易測試的代碼:使您的代碼更易於測試,幾乎每次都能改進設計。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM