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 ?
