views:

240

answers:

2

How can I simulate user input in the middle of a function called by a unit test (Using python 3's unittest)? For example, I have a function foo() who's output I'm testing. In the foo() function, it asks for user input:

x = input(msg)

And the output is based on the input:

print("input: {0}".format(x))

I would like my unit test to run foo(), enter an input and compare the result with the expected result.

+1  A: 

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.

Nick Bastin
I ended up doing something similar to this. I created a test_input function "test_input(msg)" that would override input's default behavior on the module I'm testing: "module.input = test_input". I'm setting the simulated input value with a global variable "sample_input" defined before the test_input function and set before each test case.
royvandewater
+1  A: 

Having difficulties in testing some component because of dependencies it's usually a sign of bad design. Your foo function should not depend on the global input function, but rather on a parameter. Then, when you run the program in a production environment, you wire the things in such a way that the foo is called with the result of what input returns. So foo should read:

def foo(input):
    # do something with input

This will make testing much more easier. And by the way, if your tests have IO dependencies they're no longer unit tests, but rather functional tests. Have a look on Misko Hevery's blog for more insights into testing.

Ionuț G. Stan
Maybe, but *somewhere*, someone is gathering that input. When you want to test the method doing that, you'll have to either resort to functional testing tools that manipulate a UI to test at that level, or do some injection. Of course, you should also do functional testing that manipulates the UI, but that should be used just to test the UI (e.g. that input() takes the input you provided) and not have failures bleed into the land of non-UI implementation details.
Nick Bastin
Nick, if the system under test is a legacy application, in which refactoring is very hard or it takes some time to do it, but you really want to have some characterization tests (which will actually support refactoring), then yes, I'd use your method. But, if the application is under development, it should be refactored so that unit tests are easy to write.
Ionuț G. Stan
@Nick Bastin: "Maybe, but somewhere, someone is gathering that input. When you want to test the method doing that..." The `foo(input)` does that. There is no "somewhere" else that this needs to be tested.
S.Lott
@S.Lott: I'm not sure why you think the code that calls the standard library input call doesn't need to be tested. Yes, in this *super trivial* case, that code might just call input and call your foo(input) method, but many instances of user interaction aren't that simple. (And you obviously still need to be testing that code through some way of manipulating the UI).
Nick Bastin