How to Simulate Input to Stdin for Pyunit

How can I simulate input to stdin for pyunit?

The short answer is to monkey patch raw_input().

There are some good examples in the answer to How to display the redirected stdin in Python?

Here is a simple, trivial example using a lambda that throws away the prompt and returns what we want.

System Under Test

cat ./name_getter.py
#!/usr/bin/env python

class NameGetter(object):

def get_name(self):
self.name = raw_input('What is your name? ')

def greet(self):
print 'Hello, ', self.name, '!'

def run(self):
self.get_name()
self.greet()

if __name__ == '__main__':
ng = NameGetter()
ng.run()

$ echo Derek | ./name_getter.py
What is your name? Hello, Derek !

Test case:

$ cat ./t_name_getter.py
#!/usr/bin/env python

import unittest
import name_getter

class TestNameGetter(unittest.TestCase):

def test_get_alice(self):
name_getter.raw_input = lambda _: 'Alice'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Alice')

def test_get_bob(self):
name_getter.raw_input = lambda _: 'Bob'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Bob')

if __name__ == '__main__':
unittest.main()

$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

How can I test the standard input and standard output in Python Script with a Unittest test?

Typical techniques involve mocking the standard sys.stdin and sys.stdout with your desired items. If you do not care for Python 3 compatibility you can just use the StringIO module, however if you want forward thinking and is willing to restrict to Python 2.7 and 3.3+, supporting for this both Python 2 and 3 in this way becomes possible without too much work through the io module (but requires a bit of modification, but put this thought on hold for now).

Assuming you already have a unittest.TestCase going, you can create a utility function (or method in the same class) that will replace sys.stdin/sys.stdout as outlined. First the imports:

import sys
import io
import unittest

In one of my recent projects I've done this for stdin, where it take a str for the inputs that the user (or another program through pipes) will enter into yours as stdin:

def stub_stdin(testcase_inst, inputs):
stdin = sys.stdin

def cleanup():
sys.stdin = stdin

testcase_inst.addCleanup(cleanup)
sys.stdin = StringIO(inputs)

As for stdout and stderr:

def stub_stdouts(testcase_inst):
stderr = sys.stderr
stdout = sys.stdout

def cleanup():
sys.stderr = stderr
sys.stdout = stdout

testcase_inst.addCleanup(cleanup)
sys.stderr = StringIO()
sys.stdout = StringIO()

Note that in both cases, it accepts a testcase instance, and calls its addCleanup method that adds the cleanup function call that will reset them back to where they were when the duration of a test method is concluded. The effect is that for the duration from when this was invoked in the test case until the end, sys.stdout and friends will be replaced with the io.StringIO version, meaning you can check its value easily, and don't have to worry about leaving a mess behind.

Better to show this as an example. To use this, you can simply create a test case like so:

class ExampleTestCase(unittest.TestCase): 

def test_example(self):
stub_stdin(self, '42')
stub_stdouts(self)
example()
self.assertEqual(sys.stdout.getvalue(), '42\n')

Now, in Python 2, this test will only pass if the StringIO class is from the StringIO module, and in Python 3 no such module exists. What you can do is use the version from the io module with a modification that makes it slightly more lenient in terms of what input it accepts, so that the unicode encoding/decoding will be done automatically rather than triggering an exception (such as print statements in Python 2 will not work nicely without the following). I typically do this for cross compatibility between Python 2 and 3:

class StringIO(io.StringIO):
"""
A "safely" wrapped version
"""

def __init__(self, value=''):
value = value.encode('utf8', 'backslashreplace').decode('utf8')
io.StringIO.__init__(self, value)

def write(self, msg):
io.StringIO.write(self, msg.encode(
'utf8', 'backslashreplace').decode('utf8'))

Now plug your example function plus every code fragment in this answer into one file, you will get your self contained unittest that works in both Python 2 and 3 (although you need to call print as a function in Python 3) for doing testing against stdio.

One more note: you can always put the stub_ function calls in the setUp method of the TestCase if every single test method requires that.

Of course, if you want to use various mocks related libraries out there to stub out stdin/stdout, you are free to do so, but this way relies on no external dependencies if this is your goal.


For your second issue, test cases have to be written in a certain way, where they must be encapsulated within a method and not at the class level, your original example will fail. However you might want to do something like this:

class Test(unittest.TestCase):

def helper(self, data, answer, runner):
stub_stdin(self, data)
stub_stdouts(self)
runner()
self.assertEqual(sys.stdout.getvalue(), answer)
self.doCleanups() # optional, see comments below

def test_various_inputs(self):
data_and_answers = [
('hello', 'HELLOhello'),
('goodbye', 'GOODBYEgoodbye'),
]

runScript = upperlower # the function I want to test

for data, answer in data_and_answers:
self.helper(data, answer, runScript)

The reason why you might want to call doCleanups is to prevent the cleanup stack from getting as deep as all the data_and_answers pairs are there, but that will pop everything off the cleanup stack so if you had any other things that need to be cleaned up at the end this might end up being problematic - you are free to leave that there as all of the stdio related objects will be restored at the end in the same order, so the real one will always be there. Now the function I wanted to test:

def upperlower():
raw = raw_input()
print (raw.upper() + raw),

So yes, a bit of explanation for what I did might help: remember within a TestCase class, the framework relies strictly on the instance's assertEqual and friends for it to function. So to ensure testing being done at the right level you really want to call those asserts all the time so that helpful error messages will be shown at the moment the error occurred with the inputs/answers that didn't quite show up right, rather than until the very end like what you did with the for loop (that will tell you something was wrong, but not exactly where out of the hundreds and now you are mad). Also the helper method - you can call it anything you want, as long as it doesn't start with test because then the framework will try to run it as one and it will fail terribly. So just follow this convention and you can basically have templates within your test case to run your test with - you can then use it in a loop with a bunch of inputs/outputs like what I did.

As for your other question:

only I've had a little problem importing StringIO, because I was doing import StringIO and I needed to import like from StringIO import StringIO (I don't understand really why), but be that as It may, it works.

Well, if you look at my original code I did show you how did import io and then overrode the io.StringIO class by defining class StringIO(io.StringIO). However it works for you because you are doing this strictly from Python 2, whereas I do try to target my answers to Python 3 whenever possible given that Python 2 will (probably definitely this time) not be supported in less than 5 years. Think of the future users that might be reading this post who had similar problem as you. Anyway, yes, the original from StringIO import StringIO works, as that's the StringIO class from the StringIO module. Though from cStringIO import StringIO should work as that imports the C version of the StringIO module. It works because they all offer close enough interfaces, and so they will basically work as intended (until of course you try to run this under Python 3).

Again, putting all this together along with my code should result in a self-contained working test script. Do remember to look at documentation and follow the form of the code, and not invent your own syntax and hoping things to work (and as for exactly why your code didn't work, because the "test" code was defined at where the class was being constructed, so all of that was executed while Python was importing your module, and since none of the things that are needed for the test to run are even available (namely the class itself doesn't even exist yet), the whole thing just dies in fits of twitching agony). Asking questions here help too, even though the issue you face is something really common, not having a quick and simple name to search for your exact problem does make it difficult to figure out where you went wrong, I supposed? :) Anyway good luck, and good on you for taking the effort to test your code.


There are other methods, but given that the other questions/answers I looked at here at SO doesn't seem to help, I hope this one this. Other ones for reference:

  • How to supply stdin, files and environment variable inputs to Python unit tests?
  • python mocking raw input in unittests

Naturally, it bares repeating that all of this can be done using unittest.mock available in Python 3.3+ or the original/rolling backport version on pypi, but given that those libraries hides some of the intricacies on what actually happens, they may end up hiding some of the details on what actually happens (or need to happen) or how the redirection actually happens. If you want, you can read up on unittest.mock.patch and go down slightly to the StringIO patching sys.stdout section.

Python 3 unittest simulate user input

I have this problem regularly when I'm trying to test code that brings up dialogs for user input, and the same solution should work for both. You need to provide a new function bound to the name input in your test scope with the same signature as the standard input function which just returns a test value without actually prompting the user. Depending on how your tests and code are setup this injection can be done in a number of ways, so I'll leave that as an exercise for the reader, but your replacement method will be something simple like:

def my_test_input(message):
return 7

Obviously you could also switch on the contents of message if that were relevant, and return the datatype of your choice of course. You can also do something more flexible and general that allows for reusing the same replacement method in a number of situations:

def my_test_input(retval, message):
return retval

and then you would inject a partial function into input:

import functools
test_input_a = functools.partial(my_test_input, retval=7)
test_input_b = functools.partial(my_test_input, retval="Foo")

Leaving test_input_a and test_input_b as functions that take a single message argument, with the retval argument already bound.

How to test a function with input call?

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 input

class 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 mock
import module

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

How to supply stdin, files and environment variable inputs to Python unit tests?

All three situations you've described are where you need to specifically go out of your way to ensure you are using loose coupling in your design.

Do you really need to unit test Python's raw_input method? The open method? os.environ.get? No.

You need to set up your design so that you can substitute other ways of retrieving that input. Then, during your unit tests, you'll throw in a stub of some sort that doesn't actually call raw_input or open.

For instance, your normal code might be something like:

import os
def say_hello(input_func):
name = input_func()
return "Hello " + name

def prompt_for_name():
return raw_input("What is your name? ")

print say_hello(prompt_for_name)
# Normally would pass in methods, but lambdas can be used for brevity
print say_hello(lambda: open("a.txt").readline())
print say_hello(lambda: os.environ.get("USER"))

The session looks like:


What is your name? somebody
Hello somebody
Hello [some text]

Hello mark

Then your test will be like:

def test_say_hello():
output = say_hello(lambda: "test")
assert(output == "Hello test")

Keep in mind that you should not have to test a language's IO facilities (unless you're the one designing the language, which is a different situation entirely).

python mocking raw input in unittests

You can't patch input but you can wrap it to use mock.patch(). Here is a solution:

from unittest.mock import patch
from unittest import TestCase

def get_input(text):
return input(text)

def answer():
ans = get_input('enter yes or no')
if ans == 'yes':
return 'you entered yes'
if ans == 'no':
return 'you entered no'

class Test(TestCase):

# get_input will return 'yes' during this test
@patch('yourmodule.get_input', return_value='yes')
def test_answer_yes(self, input):
self.assertEqual(answer(), 'you entered yes')

@patch('yourmodule.get_input', return_value='no')
def test_answer_no(self, input):
self.assertEqual(answer(), 'you entered no')

Keep in mind that this snippet will only work in Python versions 3.3+

Mock standard input - multi line in python 3

The patch decorator will ensure the patched function always return that value, and if subsequent calls must be different, your mock object must have a way to simulate that. This ends up being much more complicated.

What you can do however is go one step lower and patch the underlying layer, which is the standard input/output layer. One common strategy that other test frameworks have done is to deal with the sys.stdin and sys.stdout objects directly. Consider this:

import unittest
from unittest.mock import patch

from io import StringIO

def compare():
a, b, c = input().strip().split(' ')
d, e, f = input().strip().split(' ')

return '%s %s' % (a, d)

class TestCompareSysStdin(unittest.TestCase):

@patch("sys.stdin", StringIO("1 2 3\n4 5 6"))
def test_compare(self):
self.assertEqual(compare(), "1 4")

Execution

$ python -m unittest foo
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Naturally, this works at a lower level, and so the option to have an iterator that returns different values on subsequent calls may be more suitable.

How to unread a single char in Python?

I found a Python module called CIn which does exactly what I want. It is available on GitHub: https://github.com/abhishekkr200802/CIn



Related Topics



Leave a reply



Submit