Mock standard input - multi line in python 3

南楼画角 提交于 2019-12-11 05:14:18

问题


I am new to python and have been using python 3 for my learning. I am using python's unit test framework to test my code.

Problem :-

The function that I need to unit test takes inputs in the following manner:-

def compare():
   a, b, c = input().strip().split(' ')
   d, e, f = input().strip().split(' ')
   # other code here

I am using the following test case to mock the input :-

class TestCompare(unittest.TestCase):

   @patch("builtins.input", lambda: "1 2 3")
   @patch("builtins.input", lambda: "4 5 6")
   def test_compare(self):
      self.assertEqual(compare(), "1 1")

The problem I am facing is that when the test case is run the variable triplets a,b,c and d,e,f have the same values - 1,2,3

I have been trying to find a way to inject the second set of inputs to run my test but in vain.

Any help regarding the above is greatly appreciated.

Solution environment :- Python 3


回答1:


The patch decorator will ensure the patched function always return that value, and if subsequent calls must be different, your mock object must have a way to simulate that. This ends up being much more complicated.

What you can do however is go one step lower and patch the underlying layer, which is the standard input/output layer. One common strategy that other test frameworks have done is to deal with the sys.stdin and sys.stdout objects directly. Consider this:

import unittest
from unittest.mock import patch

from io import StringIO

def compare():
    a, b, c = input().strip().split(' ')
    d, e, f = input().strip().split(' ')

    return '%s %s' % (a, d)

class TestCompareSysStdin(unittest.TestCase):

    @patch("sys.stdin", StringIO("1 2 3\n4 5 6"))
    def test_compare(self):
        self.assertEqual(compare(), "1 4")

Execution

$ python -m unittest foo
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Naturally, this works at a lower level, and so the option to have an iterator that returns different values on subsequent calls may be more suitable.




回答2:


You can't patch it twice like that. You'll have to patch it once, with an object that returns different values on subsequent calls. Here's an example:

fake_input = iter(['1 2 3', '4 5 6']).__next__

@patch("builtins.input", fake_input)
def test_compare(self):
    ...



回答3:


You can't patch out your function twice like that. When you are looking to mock the same function, and have it return different values each time it is called, you should use side_effect.

side_effect takes a list of values, where each value in the list is the return of every time that function is called in your code:

class TestCompare(unittest.TestCase):

    @patch("builtins.input", side_effect=["1 2 3", "4 5 6"])
    def test_compare(self, mock_input):
        self.assertEqual(compare(), "1 1") 


来源:https://stackoverflow.com/questions/39908390/mock-standard-input-multi-line-in-python-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!