2

I am writing some tests that rely on user input to decide whether they have passed.

I have this function:

def viewable(actual_proj):
    print("\nCan you see %s projects named:\n"%len(actual_proj))
    for i in actual_proj:
        print (i+"\n")
    return input("(y/n)? : ")

Within :

def is_present(pytestconfig, project_not_present = 0):

    actual_projects = all_projects.copy()
    if (project_not_present!=0):
        del_file = all_ini_files[project_not_present-1]
        os.rename(del_file, del_file +'_tst')
        del actual_projects[project_not_present-1]
    capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')

    subprocess.Popen('./MultiPRM.exe')
    capmanager.suspendcapture(in_=True)

    decision = viewable(actual_projects)
    capmanager.resumecapture()
    if (project_not_present!=0):
        os.rename(del_file+'_tst', del_file)
    if (decision =='y'):
        return True
    else:
        return False

When i run the command pytest name_of_test_file.py it runs fine, and stops after each test to get user input. However, i want to use a file which sets up various variables and headers for a log file (called run_tests.py)

# start the report
print("Creating test report: " + os.path.abspath(report_filepath))
rep = open(report_filepath, "w")
rep.write(report_header)
rep.write("Test environment: \n");
rep.write("  Username: " + os.environ['USERNAME'] + "\n")
rep.write("Testing started at: " + get_time() + "\n\n")
rep.close()

# get application version
cmd = exe_under_test + " --help >> " + report_filepath
os.system(cmd)

# start the tests
cmd = "pytest >> " + report_filepath 
os.system(cmd)

# finalise the report
rep = open(report_filepath, "a+")
rep.write("\nTesting completed at: " + get_time() + "\n\n")
rep.close()

When i run it this way, it does not stop or run any of the tests.

If i could write to a log file while also writing the same thing to the terminal(including user input) that would be great. Otherwise, a way of calling this function correctly would work too.

7
  • 3
    The whole point of unittests is that they don't require user interaction... Commented Sep 4, 2017 at 11:18
  • The only way this function can be tested is this way, as incorrect as it may be Commented Sep 4, 2017 at 11:40
  • 1
    You really need to find a way to simulate user input for the purposes of the test. If you're relying on user input during the test, then someone else running your test may not be testing the same thing as you. Tests are supposed to be deterministic. Commented Sep 4, 2017 at 12:11
  • @PeterKentish write your own implemention of input() that returns deterministic results. Commented Sep 4, 2017 at 12:31
  • @NilsWerner what do you mean? It is testing something in a GUI, and I have no GUI testing software. Commented Sep 4, 2017 at 12:41

2 Answers 2

2

Your tests should be as easy to run as possible to make it hassle free to execute them. If they will be dependent on external (e.g. user's) input and some other tricks to properly run, then nobody is going to execute them in the long run.

If you are the sole developer in the project you can probably live with it, but I would say that this approach is incorrect and not considered to be the best-practices.

First of all if you are just waiting for user's input in the console (which seems like from your code snippets), then just mock the input built-in and set it return value, e.g.:

app.py

def my_input(prompt=''):
    try:
        return raw_input(prompt)
    except NameError:
        return input(prompt)


def my_great_func():
    choice = my_input('y/n?: ')
    return choice

test.py

import unittest.mock as mock    
import pytest

from app import my_great_func

@pytest.yield_fixture
def fake_input():
    with mock.patch('app.my_input') as m:
        yield m


def test_my_great_func(fake_input):
    fake_input.return_value = 'y'
    assert my_great_func() == 'y'

Test execution:

$ pytest test.py -v
============================= test session starts ==============================
platform linux -- Python 3.5.2, pytest-3.2.1
cachedir: .cache
rootdir: /home/kris/projects/tmp, inifile:
collected 1 item                                                                

test.py::test_my_great_func PASSED

=========================== 1 passed in 0.01 seconds ===========================

And secondly strive to write code where your application logic and GUI are loosely coupled - this way you can test your logic regardless of the GUI (be it web, desktop or mobile app).

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

1 Comment

There are some use cases for user inputs while running tests, e.g. test results can be easily judged by calling some function like hardware testing
1

Unit test shouldn't require inputs but if you need it temporarily this is a way. If you use the flag -s it will output as it goes rather than capturing and only displaying on failures. So if you use pytest -s you can call input() and it will pause and wait for input before proceding.

Comments

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.