Writing pytest tests against tools written with argparse

I usually build command-line tools using Click (and my click-app cookiecutter template), which includes a really nice set of tools for writing tests.

Today I decided to try building a tool called stream-delay using argparse from the Python standard library, since it didn't need any other dependencies.

The one challenge I had was how to write the tests. I used pytest as a test-only dependency.

Here's the pattern I came up with, using the capsys pytest fixture to capture standard output from my tool.

from stream_delay import main
import pytest

@pytest.mark.parametrize("option", ("-h", "--help"))
def test_help(capsys, option):
    try:
        main([option])
    except SystemExit:
        pass
    output = capsys.readouterr().out
    assert "Stream one or more files with a delay" in output

My main() function starts like this:

import argparse, sys

parser = argparse.ArgumentParser(
    description="Stream one or more files with a delay between each line"
)
parser.add_argument("files", type=argparse.FileType("r"), nargs="*", default=["-"])
parser.add_argument("-d", "--delay-in-ms", type=int, default=100)


def main(args=None):
    parsed_args = parser.parse_args(args)
    delay_in_s = float(parsed_args.delay_in_ms) / 1000
    # ...

As you can see, main() takes an optional list of arguments. The default for that is None which will cause argparse to read sys.argv - but I can inject arguments to the function from my tests if I need to.

I'm catching the SystemExit exception because this will be raised by default if you use -h or --help - but I still want to finish my test execution so I can inspect the captured output.

Complete code:

Created 2022-01-08T12:42:34-08:00 · Edit