How to Mock Click CLI App Subcommands in Python
Simulate Click-style CLI subcommand calls in Python by using argparse with subparsers and mocking sys.argv in tests or scripts.
Python code
33 linesimport sys
import argparse
def do_greet(args):
print(f"Hello, {args.name}!")
def do_goodbye(args):
print(f"Goodbye, {args.name}!")
def main():
parser = argparse.ArgumentParser(prog="clickapp")
subparsers = parser.add_subparsers(dest="command", required=True)
greet_parser = subparsers.add_parser("greet", help="greet someone")
greet_parser.add_argument("name", help="name to greet")
greet_parser.set_defaults(func=do_greet)
bye_parser = subparsers.add_parser("goodbye", help="say goodbye")
bye_parser.add_argument("name", help="name to say goodbye to")
bye_parser.set_defaults(func=do_goodbye)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
sys.argv = ["clickapp", "greet", "Alice"]
main()
sys.argv = ["clickapp", "goodbye", "Bob"]
main()
Output
Hello, Alice!
Goodbye, Bob!
How it works
This example mimics Click's subcommand pattern using Python's stdlib argparse, which is handy for testing CLI logic without installing third-party packages. The argparse subparsers map each command to a function via set_defaults, and args.func(args) dispatches to the right handler. By temporarily replacing sys.argv before calling main(), you simulate different command-line invocations in a script or test. This approach keeps dependencies minimal while preserving the same user experience Click provides.
Common mistakes
- Forgetting `required=True` in `add_subparsers`, which lets `main()` run without a command.
- Not setting `dest` or using `set_defaults`, causing `args.func` to be missing.
- Mutating `sys.argv` without restoring it afterward in tests, affecting other tests.
- Assuming `click.testing.CliRunner` is the only way to mock CLI calls — this works with plain argparse too.
Variations
- Use `click.testing.CliRunner` in a test with a real Click-created CLI group.
- Write a unit test that monkeypatches `sys.argv` and calls `main()` directly.
Real-world use cases
- Unit testing a CLI tool's subcommands without shelling out or needing a virtual terminal.
- Simulating multiple CLI invocations in a CI script to verify help text and error handling.
- Embedding CLI calls in a Python automation harness that runs different commands sequentially.
Sponsored
More from Modern tooling
- Build a Recipe Runner Mock in Python easy
- Build a Textual TUI App Skeleton in Python easy
- Configure ruff linter rules in pyproject.toml with Python easy
- Data Conversion Helper Functions in Python easy
- How to Bind and Mock structlog Context in Python medium
- How to Build a Chainable Filter Helper in Python easy
Keep learning
Related tutorials and quizzes for this topic.