How to test an interactive python application using pytest

Viewed 533

I have this function definition :

def interactive_mode():
    parking_query = Query()

    while(True):
        query = input()
        if query == 'exit':
            return -1
        else:
            parking_query.process(query)

I need to test this function using pytest, how can mock the input() in my tests file when it is inside an infinite loop?

Also, after mocking the input I need to capture the output to each input from stdout which could be done using capsys in pytest.

Things that I have tried doing :

1.

monkeypatch.setattr('sys.stdin', io.StringIO(query))

This gives and EOF error, doesn't work!

2.

from unittest.mock import Mock

mock_stdin = Mock()
mock_stdin.readline = Mock(io.StringIO(query))

monkeypatch.setattr('sys.stdin', io.StringIO(query))

Get's stuck infinitely when I run pytest

My test function:

def test_interactive_mode(capsys, monkeypatch):
    '''
        Tests the interactive_mode() method by mocking
        stdin using monkeypatch and reading stdout using capsys
    '''
    testcases = [
        {
            'input':  [
                        'input query'
                      ],
            'output': [
                        'expected output response to query'
                      ] 
        }
    ]

    for case in testcases:
        for i, query in enumerate(case['input']):
            with mock.patch.object(builtins, 'input', lambda : query):
                # mock_stdin = Mock()
                # mock_stdin.readline = Mock(io.StringIO(query))

                # monkeypatch.setattr('sys.stdin', io.StringIO(query))

                return_value = interactive_mode()

                if return_value == -1:
                    break

                captured = capsys.readouterr()

                assert captured.out == case['output'][i] + '\n'
1 Answers

Found a hacky way to solve the problem, serves the purpose but wouldn't suggest as the perfect solution!

def test_interactive_mode(capsys, monkeypatch):
    '''
        Tests the interactive_mode() method by mocking
        stdin using monkeypatch and reading stdout using capsys
    '''
    testcases = [
        {
            'input':  [
                        'input query'
                      ],
            'output': [
                        'expected output response to query'
                      ] 
        }
    ]

    for case in testcases:
        for i, query in enumerate(case['input']):
            with mock.patch('builtins.input', side_effect=[query, 'exit']):
                return_value = interactive_mode()

                if return_value == -1:
                    break

                captured = capsys.readouterr()

                assert captured.out == case['output'][i] + '\n'
Related