How to test a Python program with command-line arguments in pytest

Recently I wanted to test how a Python program behaved with various combinations of command-line arguments.

Thanks to pytest's mocker fixture, this is easy to do:

def test_arguments_from_cli(mocker):
    """Test whether arguments from the command line are set up correctly in a HermesApp object."""
    app = HermesApp("Test arguments in init", mqtt_client=mocker.MagicMock())

    assert == "rhasspy.home"
    assert app.args.port == 8883
    assert app.args.tls is True
    assert app.args.username == "rhasspy-hermes-app"
    assert app.args.password == "test"

You just patch the sys.argv variable with the command-line arguments you want to test your program with. This variable is a list of strings representing the arguments as separated by a space, and with the command's name as the first element. So this example function patches sys.argv to behave as if the test function is running in a command you started as follows:

$ rhasspy-hermes-app-test --host rhasspy.home --port 8883 --tls --username rhasspy-hermes-app --password test

In this case the HermesApp object uses the argparse library to populate some attributes with values from sys.argv, such as host, port, tls, username and password. The function test_arguments_from_cli tests whether these attributes are initialized correctly. But you can use the same approach if you have a main function for your command that reads your command-line arguments.

If you want to test other combinations of command-line arguments, you just add another test function that patches sys.argv with another list of arguments. All these test functions are executed independently with their own patched argument list.