How to make pytest wait for (manual) user action?

前端 未结 5 1802
抹茶落季
抹茶落季 2021-02-13 15:36

We are sucessfully using pytest (Python 3) to run a test suite testing some hardware devices (electronics). For a subset of these tests, we need the tester to change the hardwa

相关标签:
5条回答
  • 2021-02-13 16:21

    So, I found a hint by a pytest dev, based on which I basically do what the capsys.disable() function does:

    @pytest.fixture(scope="module")
    def disconnect_component(pytestconfig):
        capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
    
        capmanager.suspend_global_capture(in_=True)
        input('Disconnect component, then press enter')
        capmanager.resume_global_capture()
    
        yield  # At this point all the tests with this fixture are run
    
        capmanager.suspend_global_capture(in_=True)
        input('Connect component again, then press enter')
        capmanager.resume_global_capture()
    

    This works flawlessly as far as I can see. Don't forget the in_=True bit.

    Edit: From pytest 3.3.0 (I think), capmanager.suspendcapture and capmanager.resumecapture were renamed to capmanager.suspend_global_capture and capmanager.resume_global_capture, respectively.

    0 讨论(0)
  • 2021-02-13 16:23

    Maybe it's worth noting that above solution doesn't have to be in a fixture. I've made a helper function for that:

    import pytest
    
    def ask_user_input(msg=''):
        """ Asks user to check something manually and answer a question
        """
        notification = "\n\n???\tANSWER NEEDED\t???\n\n{}".format(msg)
    
        # suspend input capture by py.test so user input can be recorded here
        capture_manager = pytest.config.pluginmanager.getplugin('capturemanager')
        capture_manager.suspendcapture(in_=True)
    
        answer = raw_input(notification)
    
        # resume capture after question have been asked
        capture_manager.resumecapture()
    
        logging.debug("Answer: {}".format(answer))
        return answer
    
    0 讨论(0)
  • 2021-02-13 16:25

    For future reference, if you need to use input with pytest. You can do this in any part of your pytest, setup_class, test_..., teardown_method, etc. This is for pytest > 3.3.x

    import pytest
    
    capture_manager = pytest.config.pluginmanager.getplugin('capturemanager')
    capture_manager.suspend_global_capture(in_=True)
    answer = input('My reference text here')
    capture_manager.resume_global_capture()
    
    0 讨论(0)
  • 2021-02-13 16:29

    As of pytest 5, as a fixture, you can use this:

    @pytest.fixture
    def suspend_capture(pytestconfig):
        class suspend_guard:
            def __init__(self):
                self.capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
            def __enter__(self):
                self.capmanager.suspend_global_capture(in_=True)
            def __exit__(self, _1, _2, _3):
                self.capmanager.resume_global_capture()
    
        yield suspend_guard()
    

    Example usage:

    def test_input(suspend_capture):
        with suspend_capture:
            input("hello")
    
    0 讨论(0)
  • 2021-02-13 16:29

    Solutions that use the global pytest.config object no longer work. For my use case, using --capture=sys together with a custom input() that uses stdin and stdout directly works well.

    
    def fd_input(prompt):
        with os.fdopen(os.dup(1), "w") as stdout:
            stdout.write("\n{}? ".format(prompt))
    
        with os.fdopen(os.dup(2), "r") as stdin:
            return stdin.readline()
    
    0 讨论(0)
提交回复
热议问题