Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing argument using Python Click

I have a command-line script with Python-click with an argument and option:

# console.py
import click

@click.command()
@click.version_option()
@click.argument("filepath", type=click.Path(exists=True), default=".")
@click.option(
    "-m",
    "--max-size",
    type=int,
    help="Max size in megabytes.",
    default=20,
    show_default=True,
)
def main(filepath: str, max_size: int) -> None:
    max_size_bytes = max_size * 1024 * 1024  # convert to MB
    if filepath.endswith(".pdf"):
        print("success")
    else:
        print(max_size_bytes)

Both the argument and option have default values and work on the command-line and using the CLI it behaves as expected. But when I try testing it following Click documentation and debug it, it does not enter the first line:

# test_console.py
from unittest.mock import Mock

import click.testing
import pytest
from pytest_mock import MockFixture

from pdf_split_tool import console

@pytest.fixture
def runner() -> click.testing.CliRunner:
    """Fixture for invoking command-line interfaces."""
    return click.testing.CliRunner()

@pytest.fixture
def mock_pdf_splitter_pdfsplitter(mocker: MockFixture) -> Mock:
    """Fixture for mocking pdf_splitter.PdfSplitter."""
    return mocker.patch("pdf_split_tool.pdf_splitter.PdfSplitter", autospec=True)

def test_main_uses_specified_filepath(
    runner: click.testing.CliRunner,
    mock_pdf_splitter_pdfsplitter: Mock, 
) -> None:
    """It uses the specified filepath."""
    result = runner.invoke(console.main, ["test.pdf"])
    assert result.exit_code == 0

I couldn't see why it is giving since the debugger did not enter the first line of function main(). Any ideas of what could be wrong?

like image 938
staticdev Avatar asked May 30 '20 22:05

staticdev


People also ask

What is Click option in Python?

Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the “Command Line Interface Creation Kit”. It's highly configurable but comes with sensible defaults out of the box.

What is First Click testing?

First Click Testing examines what a test participant would click on first on the interface in order to complete their intended task. It can be performed on a functioning website, a prototype or a wireframe.


2 Answers

The failure is due to following error.

(pdb)print result.output
"Usage: main [OPTIONS] [FILEPATH]\nTry 'main --help' for help.\n\nError: Invalid value for '[FILEPATH]': Path 'test.pdf' does not exist.\n"

This is happening due to following code in console.py which checks if the filepath exists. @click.argument("filepath", type=click.Path(exists=True), default=".")

One way to test creating a temporary file is using afterburner's code:

# test_console.py
def test_main_uses_specified_filepath() -> None:
    runner = click.testing.CliRunner()
    with runner.isolated_filesystem():
        with open('test.pdf', 'w') as f:
            f.write('Hello World!')

        result = runner.invoke(main, ["test.pdf"])
    assert result.exit_code == 0
like image 184
apoorva kamath Avatar answered Oct 01 '22 21:10

apoorva kamath


I've changed your test method to the following. However, this is more an augmentation to apoorva kamath's answer.


def test_main_uses_specified_filepath() -> None:
    runner = click.testing.CliRunner()
    with runner.isolated_filesystem():
        with open('test.pdf', 'w') as f:
            f.write('Hello World!')

        result = runner.invoke(main, ["test.pdf"])
    assert result.exit_code == 0

Simply put, it creates an isolated file system that gets cleaned up after the text is executed. So any files created there are destroyed with it.

For more information, Click's Isolated Filesystem documentation might come in handy.

Alternatively, you can remove the exists=True parameter to your file path.

like image 45
afterburner Avatar answered Oct 01 '22 21:10

afterburner