1

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?

1 Answer 1

3

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"
Sign up to request clarification or add additional context in comments.

2 Comments

When I run pytest test.py I am getting NameError: name 'patch' is not defined with your code, and if I remove the line @patch('sample.input'), I am getting fixture 'mock_input' not found, any ideas?
NVM after from unittest.mock import patch it runs, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.