3

I've got a program that's built on functions that taks user inputs inside the functions and not parameters before the function: for example, say my function is

def my_function():
    a = input("a: ")
    b = input("b: ")
    print(a+b)

and from what I understand thus far about unit testing a function like that is harder to unit test than a function that works for example like this:

def another_function(a,b):
    return(a+b)

So how do I go about testing a function that looks like my_function, for example? It feels as if it would be easy to test manually by just entering incorrect inputs and checking for errors, but I have to write a test suite that tests all my functions automatically.

5
  • 1
    You can patch input to provide whatever values you want, or set input=input in the function definition and manually inject something to replace the built-in input (see e.g. this code I tested with the latter method). Commented May 15, 2015 at 12:21
  • Ah, okay, so I could create a test method/function that has the variable 'a' predefined so it uses that in the test suite? It could for example assert if print("papajohn") is equal to my_function() with "papa" as a and "john" as b? Commented May 15, 2015 at 12:25
  • Not really, you also need to patch print in your case, as your functions don't return anything (or, much better, restructure them to return and print elsewhere). You need to have a mock stdout then make sure that the text you were expecting got passed to it. Commented May 15, 2015 at 12:38
  • Okay, so the moral of the story is you can't really test a function without a return value? Commented May 15, 2015 at 12:50
  • It's much more difficult, certainly! At least in Python 3 print is a function, which makes life a bit easier than with Python 2. Commented May 15, 2015 at 12:51

1 Answer 1

1

Given that your function input comes from input and output goes to print, you will have to "mock" both of these functions to test my_function. For example, using simple manual mocking:

def my_function(input=input, print=print):
    a = input("a: ")
    b = input("b: ")
    print(a+b)

if __name__ == '__main__':
    inputs = ['hello', 'world']
    printed = []

    def mock_input(prompt):
        return inputs.pop(0)

    def mock_print(text):
        printed.append(text)

    my_function(mock_input, mock_print)
    assert len(inputs) == 0, 'not all input used'
    assert len(printed) == 1, '{} items printed'.format(len(printed))
    assert printed[0] == 'helloworld'

When you compare this to:

assert my_function('hello', 'world') == 'helloworld'

you can see why the latter is much preferred!

You could also use a proper mocking library to do this more neatly, without having to supply the functions as arguments; see e.g. How to supply stdin, files and environment variable inputs to Python unit tests?.

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, I see.. Maybe I should just rewrite my functions in my main program a bit so they return a value that I can use for my unit test, seeing as that little program took that much code to test. My programs are way longer than that and has several inputs along the way, so maybe that'll just be easier.. Thanks a lot for the help!

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.