I have a function called get_choices()
- that makes an api call - that is called inside a click.Choice()
decorator for a click.Command()
function called cli()
.
I want to test the command function cli()
using click's CliRunner().invoke()
method but avoid the api call inside get_choices()
.
Below is a minimal code to use. Make sure to have click installed using pip install click
.
cli.py
from click import command, option, Choice
def get_choices():
print("making an api call")
return ["name_1", "name_2"]
@command()
@option(
"--name",
prompt=True,
type=Choice(choices=get_choices()), # api call is made here
)
def cli(name):
pass # Doesn't matter what happens here
test_cli.py
from unittest.mock import patch
import click
from click.testing import CliRunner
# This avoids the api call when importing cli below
# but makes an api call itself when loading the module
patch("cli.get_choices", get_test_choices).start()
def test_cli_behaves():
runner = CliRunner()
import cli
# Replaces prod choices with test choices
# I want to avoid using params[0]
cli.cli.params[0].type = click.Choice(["name_test"])
result = runner.invoke(cli.cli, args=["--name", "name_test"])
print(result.stdout)
assert result.exit_code == 0
assert False
Is there a way to patch get_choices()
before it's called inside the decorator when loading the module ?
I would like to avoid:
- Any api call
- Accessing
cli.cli.params[0]
since it's not clean and becomes cumbersome when there are many options added to the cli.
I am testing by attaching a debugger to the test and adding a breakpoint on the api call in get_choices()
and can confirm an api call is made on the patch line or the import line if the patch isn't used.
I've read a lot of questions/answers about patching with decorators on stackoverflow, most notably this one which I've used to inspire my current solution, but none provides what I really want.
Edit: Does the answer to this question also apply in my case ?