Programming · free software · the open webSubscribe via RSS

How Should You Test a Command-Line Tool’s Exit Codes?

From the earlier reference collection. Read the current software blog.

Exercise success, invalid input, missing source, and processing failure, then check both exit status and user-facing output.

Write down the command contract

List the outcomes the command exposes before writing tests. A tool may use zero for success and distinct nonzero statuses for invalid arguments, unavailable input, or a processing error. If the project documents only success versus failure, test that stable distinction rather than inventing a detailed numeric scheme.

Run the executable as a subprocess when practical. This checks argument parsing, process termination, and output streams together. Calling an internal function directly can miss a wrapper that always exits successfully or converts exceptions into the wrong status.

Exercise success and bad input

Use a tiny valid fixture and assert successful completion, the documented status, and any expected output artifact. Then pass an invalid option or malformed value and verify that the tool returns a failure status, explains the problem on standard error, and does not produce a misleading success file.

Keep assertions focused on useful stable phrases or structured output. Exact matching of an entire usage paragraph can make tests brittle when wording changes, while checking only that the command failed may miss a confusing or empty diagnostic.

Separate missing and broken sources

Test a source that does not exist and a source that exists but cannot be processed. These exercise different paths: opening the input can fail before parsing, while a malformed file reaches the processing layer. Assert the appropriate status and a concise message for each case.

Use temporary directories and synthetic files so the test does not depend on machine-specific paths or permissions. Confirm that errors do not print secrets from environment variables or full internal tracebacks when the command is designed for ordinary users.

Check cleanup and repeatability

After a failed run, inspect whether partial outputs are removed or clearly marked incomplete according to the tool’s contract. Then run a successful case in the same temporary workspace to catch stale state that masks subsequent behavior.

The full matrix makes exit behavior part of the command’s interface. A user or shell script can distinguish completion from actionable failure, while maintainers can change internal code without losing predictable process-level behavior.

References

  1. Python documentation

Suggest a correction · All posts