Home > database >  How to test interactive python script with multiple `input` functions
How to test interactive python script with multiple `input` functions

Time:04-16

I know how to test python functions with a single input function, but I can't seem to figure out how to test python functions with multiple input functions in it.

Please see the below minimal example code test.py below:

import pytest
import mock
import builtins


def unsubscribe():
    if input("are you unsubscribing? [y/n]") == "n":
        return "we are glad to have you back"
    else:
        if input("would you like a 20%% off discount code? [y/n]") == "y":
            return "your discount code is SAVE20, and we are glad you have you back"
        else:
            return "we are sad to see you go"


def test_unsubscribe():
    with mock.patch.object(builtins, 'input', lambda _: 'n'):
        assert unsubscribe() == "we are glad to have you back"
    with mock.patch.object(builtins, 'input', lambda _: 'y'):
        assert unsubscribe() == "your discount code is SAVE20, and we are glad you have you back"
    # what to put here to test the below
    #    assert unsubscribe() == "we are sad to see you go"

With the current approach, the mock patch replaces every single input function as all n or all y, which causes the control flow of first input y and second input n unreachable. What is the right way to unit test a python function with multiple input functions in it?

CodePudding user response:

You can use side_effect for this. Side_effect takes a list as input and provides you the result in the order of elements.

Assuming you have the test and source code in separate directories

@patch('sample.input')
def test_options(mock_input):
   mock_input.side_effect = ['y', 'y']
   result = unsubscribe()
   assert result == "your discount code is SAVE20, and we are glad you have you back"

You can take this to the next step by defining the exact output you want for your use case, instead of arbitrarily passing yes and no values

@patch('sample.input')
def test_options(mock_input):

   def input_side_effect(*args, **kwargs):
       m = {
           'are you unsubscribing? [y/n]': 'y',
           'would you like a 20%% off discount code? [y/n]': 'y',
       }.get(args[0])
       if m:
           return m
       pytest.fail('This question is not expected')

   mock_input.side_effect = input_side_effect
   result = unsubscribe()
   assert result == "your discount code is SAVE20, and we are glad you have you back"
  • Related