19

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)
coffee-grinder
  • 26,940
  • 19
  • 56
  • 82

4 Answers4

35

__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.

falsetru
  • 357,413
  • 63
  • 732
  • 636
  • @IlanBiala, It works for me: http://ideone.com/kdAjEd (tested on Python 3.5.1, Ubuntu 16.04 beta, WIndows 7, simplified for brevity) – falsetru Mar 01 '16 at 15:53
  • input takes one positional argument, so: `@patch('builtins.input', lambda _ : 'y')` – SeF Jan 18 '19 at 14:55
  • 1
    @SeF, Thank you for the comment. [`input`'s argument is optional](https://docs.python.org/3/library/functions.html#input); so I used `*args` instead of `_`. – falsetru Jan 18 '19 at 15:18
8

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
>>>
tbc0
  • 1,563
  • 1
  • 17
  • 21
1

For Python 2.x:

@patch('__builtin__.input')

worked for me.

Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
Harry Moreno
  • 10,231
  • 7
  • 64
  • 116
0

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.