Reputation: 27600
How do you use the @patch decorator to patch the built-in input() function?
For example, here's a function in question.py that I'd like to test, which contains a call to input():
def query_yes_no(question, default="yes"):
""" Adapted from http://stackoverflow.com/questions/3041986/python-command-line-yes-no-input """
valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
choice = input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n")
Here's my test, which gives me the error "ImportError: No module named 'builtins'":
import unittest
from unittest.mock import patch
import question
class TestQueryYesNo(unittest.TestCase):
@patch('__builtins__.input.return_value', 'y')
def test_query_y(self):
answer = question.query_yes_no("Blah?")
self.assertTrue(answer)
Upvotes: 20
Views: 22261
Reputation: 1
In my test I wanted to pass many different inputs to test match inside run, and It's works fine. Test passed.
import io
import unittest
from unittest.mock import patch
from TextApp import run
def input_args():
yield "show"
yield "switch ts1"
yield "show"
yield "exit"
class TestTextApp(unittest.TestCase):
@patch("builtins.input", side_effect=input_args())
def test_ShowSwitchTS1ShowExit(self, mock_input):
expected = (
"ToggleSwitch is: off\n"
"ToggleSwitch is: off\n"
"ToggleSwitch is: off\n"
"LightBulb is: off\n"
"ToggleSwitch is: on\n"
"ToggleSwitch is: off\n"
"ToggleSwitch is: off\n"
"LightBulb is: on\n"
)
with patch("sys.stdout", new=io.StringIO()) as fake_out:
run()
result = fake_out.getvalue()
self.assertEqual(expected, result)
Upvotes: 0
Reputation: 3
For Python 3.8 the accepted answer didn't work for me. It didn't like the positional parameter even though my code was actually utilizing it. What worked for me was simply:
@patch('builtins.input')
Not sure if I am doing something wrong, but here you are.
Upvotes: 0
Reputation: 369064
__builtin__ module is renamed to builtins in Python 3. Replace as follow:
@patch('builtins.input', lambda *args: 'y')
UPDATE
input
has an optional parameter. updated the code to accept the optional parameter.
Upvotes: 36
Reputation: 1577
Or use Mock's return_value
attribute. I couldn't get it to work as a decorator, but here's how to do it with a context manager:
>>> import unittest.mock
>>> def test_input_mocking():
... with unittest.mock.patch('builtins.input', return_value='y'):
... assert input() == 'y'
...
>>> def test_input_mocking():
... with unittest.mock.patch('builtins.input', return_value='y'):
... assert input() == 'y'
... print('we got here, so the ad hoc test succeeded')
...
>>> test_input_mocking()
we got here, so the ad hoc test succeeded
>>>
Upvotes: 8