Roy Larson
Roy Larson

Reputation: 33

Pytest: How to test a separate function with input call?

This question has been asked here Pytest: How to test a function with input call?

But the answer by mareoraft (below) does not work for a function call it only works inside the current test function scope.

original answer:

def test_something_that_involves_user_input(monkeypatch):

    # monkeypatch the "input" function, so that it returns "Mark".
    # This simulates the user entering "Mark" in the terminal:
    monkeypatch.setattr('builtins.input', lambda x: "Mark")

    # go about using input() like you normally would:
    i = input("What is your name?")
    assert i == "Mark"

Here is test code where I moved the input to another function (this fails)

def separate_input_function():
    a = input()
    return a

def test_separate_function_monkeypatch_input(monkeypatch):
    ans = '3'
    with monkeypatch.context() as m:
        m.setattr('builtins.input', lambda prompt: ans)
        result = separate_input_function()

    assert result == ans

This raises

TypeError: <lambda>() missing 1 required positional argument: 'prompt'

Any ideas on how to get this to work?

Thanks

Upvotes: 2

Views: 2013

Answers (2)

rocksportrocker
rocksportrocker

Reputation: 7419

An alternative to patching is a technique called "dependency injection":

def separate_input_function(_input=input):
    a = _input()
    return a

def test_separate_function_monkeypatch_input(monkeypatch):
    _input = lambda: 42
    result = separate_input_function(_input=_input)
    assert result == 42

Maybe this helps.

Upvotes: 0

abarnert
abarnert

Reputation: 365747

Your problem has nothing to do with moving the input to a separate function, or even with monkeypatching; it has to do with passing the wrong number of arguments—just as the error message says.

In the example you refer to, the monkeypatching function is defined to take one parameter, and the input call passes one argument.

If your own attempt, the monkeypatching function is defined to take one parameter, but the input call passes no arguments.

You can just define it to take an optional parameter, just like the real input:

m.setattr('builtins.input', lambda prompt="": ans)

Upvotes: 3

Related Questions