Chapter02 Test Small Click

If you find this content useful, consider buying this book:

If you enjoyed this book considering buying a copy

Chapter 2: Test with Click #

Noah Gift

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 simple click 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 the file 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.py

Another 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.py

So 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.output

Like 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 ==================