Testing with Click
Chapter 2: Test with Click
Noah Gift
Learning Objectives
By the end of this chapter, you will be able to:
- Implement Click testing patterns: Use CliRunner to create comprehensive tests for command-line applications
- Apply testing best practices: Write unit tests that verify both functionality and error conditions
- Integrate test coverage: Measure and improve code coverage for CLI tools
- Design testable CLI applications: Structure Click applications for optimal testing
Prerequisites
- Previous Chapter: Chapter 1 (Click framework basics)
- Testing Knowledge: Basic understanding of unit testing concepts
- Python Skills: Functions, imports, and assertion statements
Chapter Overview
Estimated Time: 45 minutes
Hands-on Labs: 1 comprehensive testing exercise
Assessment: 5-question knowledge check
Testing command-line applications presents unique challenges compared to testing traditional functions. This chapter demonstrates how Click's testing framework makes CLI testing straightforward and reliable.
Test a small click app
Writing command-line tools is one of my favorite ways to write code. A
command-line tool is one of the simplest ways to take programming logic and
create a flexible tool. Let's walk through a simpleclick tool.
The following is an intentionally simple command-line tool that takes a flag--path (path to a file) and flag --ftype (file type). If this runs without
specifying, it will prompt a user for both of these flags.
{caption: "Simple click based cli that searches for files: gcli.py"}
#!/usr/bin/env python
import click
import glob
# this is bad code intentionally
# varbad=
@click.command()
@click.option(
"--path",
prompt="Path to search for files",
help="This is the path to search for files: /tmp",
)
@click.option(
"--ftype", prompt="Pass in the type of file", help="Pass in the file type: i.e csv"
)
def search(path, ftype):
results = glob.glob(f"{path}/*.{ftype}")
click.echo(click.style("Found Matches:", fg="red"))
for result in results:
click.echo(click.style(f"{result}", bg="blue", fg="white"))
if __name__ == "__main__":
# pylint: disable=no-value-for-parameter
search()Another useful feature of click is the --help flag comes for free and
autogenerates from the options. This following is the output from./glci --help.
{caption: "Output of ./gcli --help"}
(.tip) $ click-testing git:(master) $ ./gcli.py --help
Usage: gcli.py [OPTIONS]
Options:
--path TEXT This is the path to search for files: /tmp
--ftype TEXT Pass in the file type: i.e csv
--help Show this message and exit.Next up is to run ./gcli.py and let it prompt us for both the path and thefile type.
{caption: "Output of search with prompts"}
(.tip) $ click-testing git:(master) $ ./gcli.py
Path to search for files: .
Pass in the type of file: py
Found Matches:
./gcli.py
./test_gcli.py
./hello-click.py
./hello-click2.pyAnother run of ./gcli.py shows how it can be run by passing in the flags ahead
of time.
{caption: "Output of search with flags"}
(.tip) $ click-testing git:(master) $ ./gcli.py --path . --ftype py
Found Matches:
./gcli.py
./test_gcli.py
./hello-click.py
./hello-click2.pySo can we test this command-line tool? Fortunately, the authors of click have
an
easy solution for this as well.
In a nutshell, by using the line from click.testing import CliRunner it
invokes a test runner as shown.
{caption: "Test file: test_gcli.py"}
from click.testing import CliRunner
from gcli import search
# search(path, ftype):
def test_search():
runner = CliRunner()
result = runner.invoke(search, ["--path", ".", "--ftype", "py"])
assert result.exit_code == 0
assert ".py" in result.outputLike pytest, a simple assert statement is the only thing needed to create a
test. Two different types of assert comments show. The first assert checks
the result of the call to the command line tool and ensures that it returns a
shell exit status of 0. The second assert parses the output returned and
ensures that .py is in the production since the file type passed in is py.
When the command make test is run, it generates the following output. Again
take note of how using the convention make test simplifies the complexity of
remembering what exact flags to run. This step can set once and then forget
about it.
{caption: "Output of make test"}
(.tip) $ click-testing git:(master) make test
python -m pytest -vv --cov=gcli test_gcli.py
========================================= test session starts =================
platform darwin -- Python 3.7.6, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 --
cachedir: .pytest_cache
rootdir: /Users/noahgift/testing-in-python/chapter11/click-testing
plugins: cov-2.8.1
collected 1 item
test_gcli.py::test_search PASSED
---------- coverage: platform darwin, Python 3.7.6-final-0 -----------
Name Stmts Miss Cover
-----------------------------
gcli.py 11 1 91%
========================================== 1 passed in 0.03s ==================Advanced Testing Patterns
Beyond basic functionality testing, professional CLI applications require comprehensive test coverage for:
Error Handling Tests
- Invalid input validation
- File system errors
- Network connectivity issues
- Permission errors
Integration Tests
- End-to-end workflow testing
- External dependency mocking
- Configuration file handling
- Environment variable testing
Performance Tests
- Large file handling
- Memory usage validation
- Execution time benchmarks
- Resource cleanup verification
Chapter Summary
Testing Click applications is straightforward thanks to the CliRunner framework. Key concepts covered include:
- CliRunner Setup: Using Click's built-in testing framework for CLI applications
- Test Patterns: Writing comprehensive tests for success and error cases
- Coverage Metrics: Measuring and improving test coverage for CLI tools
- Best Practices: Structuring tests for maintainability and reliability
Proper testing ensures your CLI tools are robust, reliable, and ready for production use. The patterns learned here apply to all Click applications, from simple utilities to complex command suites.
🎓 Continue Your Learning Journey
Python Command Line Mastery
Master advanced Click patterns, testing strategies, and deployment techniques for production CLI tools.
- Advanced Click decorators and context handling
- Comprehensive CLI testing with pytest
- Packaging and distribution best practices
- Performance optimization for large-scale tools
DevOps with Python
Learn to build automation tools, deployment scripts, and infrastructure management CLIs with Python.
- Infrastructure automation with Python
- Building deployment and monitoring tools
- Integration with cloud platforms (AWS, GCP, Azure)
- Real-world DevOps CLI examples
Python Testing and Quality Assurance
Ensure your CLI tools are robust and reliable with comprehensive testing strategies.
- Unit testing Click applications
- Integration testing for CLI tools
- Mocking external dependencies
- Continuous integration for CLI projects
📚 Related Learning Paths
📝 Test Your Knowledge: Testing with Click
Take this quiz to reinforce what you've learned in this chapter.