Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.
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.
1from stream_delay import main2import pytest3
4@pytest.mark.parametrize("option", ("-h", "--help"))5def test_help(capsys, option):6 try:7 main([option])8 except SystemExit:9 pass10 output = capsys.readouterr().out11 assert "Stream one or more files with a delay" in outputMy main() function starts like this:
1import argparse, sys2
3parser = argparse.ArgumentParser(4 description="Stream one or more files with a delay between each line"5)6parser.add_argument("files", type=argparse.FileType("r"), nargs="*", default=["-"])7parser.add_argument("-d", "--delay-in-ms", type=int, default=100)8
9
10def main(args=None):11 parsed_args = parser.parse_args(args)12 delay_in_s = float(parsed_args.delay_in_ms) / 100013 # ...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: