Testing input() and print()

I really don’t get it. I also found no example in the internet.

How do I write a test for a function with a input prompt in nose?

For example this simple function:

def input_function():
    return input("What's your name? > ")

How does a test function for this code look like?
Thanks for your help.

May above that isn’t a good example.
How would you test this function?

def yes_or_no():
	answer = input("Do you want to quit? > ")
	
	if answer == "yes":
		print("Quitter!")
	elif answer == "no":
		print("Awesome!")
	else:
	    print("uhh?")
		
yes_or_no()

It might be worth looking back at one of the seminars last week as Zed covered something very similar.

There are two approaches to this; automation the input action via a tool that interacts like a human, or mock the input (i.e. hard code a value to ensure it gets passed every time). I don’t think you want to do the human interaction part, as that is usually UI testing with tools like Selenium/Appium, but mocking it nice and easy.

There are several ways to do this and below are a couple of resources (the keyword is ‘mock’ when searching):

https://docs.python.org/3/library/unittest.mock.html#module-unittest.mock

The cleanest syntax I found for mocking is this:

import mock


def yes_or_no():
    answer = input("Do you want to quit? > ")
    if answer == "yes":
        return("Quitter!")
    elif answer == "no":
        return("Awesome!")
    else:
        return("BANG!")


def test_quitting():

    with mock.patch('builtins.input', return_value="yes"):
        assert yes_or_no() == "Quitter!"

    with mock.patch('builtins.input', return_value="no"):
        assert yes_or_no() == "Awesome!"

The print statement is a real PITA so I change it to return, otherwise the output is None. I got totally trapped by the print / return thing for a while as I assume they are the same. They are not.

Hey Didier, you have the example in Zed’s seminar on refactoring your code. I think he uses pytest instead of nose.
But you can check it here, you need to test if the built-in input process has been called:
https://lab.learncodethehardway.com/zedshaw/refactorschool/blob/242974466d3bdbf92edff751cb63c738e1371272/Jun_12-23/tests/test_fight.py

Good point. I was also using Pytest. Its worth checking it out over Nose in my opinion.

I do need to look into this more. If I understand correctly, that syntax

mock.patch('builtins.input', return_value.......)

should work for any of Python’s built-in methods, not just input(). I need to play around with mocking them.

Thanks @io_io and @gpkesley. It feels good to have some fellows. I will try your suggestions and will come back to tell you about my success with it or ask more questions.

I saw zed doing it with my code but didn’t understand what he did exactly :cry:

1 Like

This is why the forum is for :smiley:
So we can ask away all the stuff we don’t understand.

Totally agree. And the good thing about this forum over some others is that we are all reasonably aware of the content each other are studying, and have similar challenges.

Something like stackoverflow is great for getting solutions to difficult problems, but often the responses can be very complicated or require pro-level understanding. Here, when someone say they can’t do something, we can all offer support by recalling what we did when we hit the same blocker.

Yes, actually remember I had to figure that out also in Refactor School S5? I had to figure out how to mock out and fake out the input function in that video so definitely watch that.

@io_io, @gpkesley and @zedshaw
I asked one month ago and you helped me in search of an answer. Thank you!
Finally I found it:

testing print()

from nose.tools import *
from unittest import mock
from unittest.mock import patch
import io


def foo():
    print("Something")


# Solution one: testing print with @patch
@patch('sys.stdout', new_callable=io.StringIO)
def test_foo_one(mock_stdout):
    foo()
    assert mock_stdout.getvalue() == 'Something\n'


# Solution two: testing print with with-statement
def test_foo_two():
    with mock.patch('sys.stdout', new=io.StringIO()) as fake_stdout:
        foo()

    assert fake_stdout.getvalue() == 'Something\n'


# Solution three: testing print with with-statement
# and assert_has_calls
def test_foo_three():
    with mock.patch('sys.stdout') as fake_stdout:
        foo()

    fake_stdout.assert_has_calls([
        mock.call.write('Something'),
        mock.call.write('\n')
    ])

testing input()

from nose.tools import *
from unittest import mock

def bar():
    ans = input("enter yes or no")
    if ans == "yes":
        return "you entered yes"
    if ans == "no":
        return "you entered no"


def test_bar_yes():
    original_input = mock.builtins.input
    mock.builtins.input = lambda _: "yes"
    assert_equal(bar(), "you entered yes")
    mock.builtins.input = original_input

def test_bar_no():
    original_input = mock.builtins.input
    mock.builtins.input = lambda _: "no"
    assert_equal(bar(), "you entered no")
    mock.builtins.input = original_input

For a better way to testing input with mock and with-statements see @gpkesley answer above.

Some usefull ressources:

https://stackoverflow.com/questions/3622455/what-is-the-purpose-of-mock-objects#3623574
https://stackoverflow.com/questions/21046717/python-mocking-raw-input-in-unittests
https://stackoverflow.com/questions/32488280/unit-testing-for-user-input-and-expected-output-in-python
https://stackoverflow.com/questions/46222661/how-to-mock-a-user-input-in-python
http://blog.thedigitalcatonline.com/blog/2016/09/27/python-mocks-a-gentle-introduction-part-2/
https://stackoverflow.com/questions/25677260/python-unit-test-for-nested-if-statement#25677484
https://stackoverflow.com/questions/15391504/how-to-unit-test-an-if-else-statement-in-c-sharp-using-just-a-stubclass
https://stackoverflow.com/questions/18161330/using-unittest-mock-to-patch-input-in-python-3
https://stackoverflow.com/questions/12998908/is-it-possible-to-mock-pythons-built-in-print-function
1 Like