How to test a function with input call? How to test a function with input call? python python

How to test a function with input call?


As The Compiler suggested, pytest has a new monkeypatch fixture for this. A monkeypatch object can alter an attribute in a class or a value in a dictionary, and then restore its original value at the end of the test.

In this case, the built-in input function is a value of python's __builtins__ dictionary, so we can alter it like so:

def test_something_that_involves_user_input(monkeypatch):    # monkeypatch the "input" function, so that it returns "Mark".    # This simulates the user entering "Mark" in the terminal:    monkeypatch.setattr('builtins.input', lambda _: "Mark")    # go about using input() like you normally would:    i = input("What is your name?")    assert i == "Mark"


You should probably mock the built-in input function, you can use the teardown functionality provided by pytest to revert back to the original input function after each test.

import module  # The module which contains the call to inputclass TestClass:    def test_function_1(self):        # Override the Python built-in input method         module.input = lambda: 'some_input'        # Call the function you would like to test (which uses input)        output = module.function()          assert output == 'expected_output'    def test_function_2(self):        module.input = lambda: 'some_other_input'        output = module.function()          assert output == 'another_expected_output'            def teardown_method(self, method):        # This method is being called after each test case, and it will revert input back to original function        module.input = input  

A more elegant solution would be to use the mock module together with a with statement. This way you don't need to use teardown and the patched method will only live within the with scope.

import mockimport moduledef test_function():    with mock.patch.object(__builtins__, 'input', lambda: 'some_input'):        assert module.function() == 'expected_output'


You can replace sys.stdin with some custom Text IO, like input from a file or an in-memory StringIO buffer:

import sysclass Test:    def test_function(self):        sys.stdin = open("preprogrammed_inputs.txt")        module.call_function()    def setup_method(self):        self.orig_stdin = sys.stdin    def teardown_method(self):        sys.stdin = self.orig_stdin

this is more robust than only patching input(), as that won't be sufficient if the module uses any other methods of consuming text from stdin.

This can also be done quite elegantly with a custom context manager

import sysfrom contextlib import contextmanager@contextmanagerdef replace_stdin(target):    orig = sys.stdin    sys.stdin = target    yield    sys.stdin = orig

And then just use it like this for example:

with replace_stdin(StringIO("some preprogrammed input")):    module.call_function()