Search code examples
pythonpytestpython-click

click.testing.CliRunner and handling SIGINT/SIGTERM signals


I want to add few tests on how my cli app handles different signals (SIGTERM, etc). And I am using native testing solution click.testing.CliRunner alongside of pytest.

Test looks pretty standard and simple

def test_breaking_process(server, runner):

    address = server.router({'^/$': Page("").exists().slow()})

    runner = CliRunner(mix_stderr=True)
    args = [address, '--no-colors', '--no-progress']
    result = runner.invoke(main, args)
    assert result.exit_code == 0

And here I am stuck, how could I send SIGTERM to process in runner.invoke? I see no problem to do it if I use e2e tests (calling executable and not CLIrunner), but I would like to try to implement this (at least able to send os.kill)

is there a way to do it?


Solution

  • So, if you want to test your click powered application for handling different signals, you can do next procedure.

    def test_breaking_process(server, runner):
    
        from multiprocessing import Queue, Process
        from threading import Timer
        from time import sleep
        from os import kill, getpid
        from signal import SIGINT
    
        url = server.router({'^/$': Page("").slow().exists()})
        args = [url, '--no-colors', '--no-progress']
    
        q = Queue()
    
        # Running out app in SubProcess and after a while using signal sending 
        # SIGINT, results passed back via channel/queue  
        def background():
            Timer(0.2, lambda: kill(getpid(), SIGINT)).start()
            result = runner.invoke(main, args)
            q.put(('exit_code', result.exit_code))
            q.put(('output', result.output))
    
        p = Process(target=background)
        p.start()
    
        results = {}
    
        while p.is_alive():
            sleep(0.1)
        else:
            while not q.empty():
                key, value = q.get()
                results[key] = value
    
        assert results['exit_code'] == 0
        assert "Results can be inconsistent, as execution was terminated" in results['output']