How do I get just a list of commands/subcommands using click in my test suite?
Asked Answered
C

1

5

I have a CLI application built that uses grouped commands and sub-commands. Everything is working. However, I want to ensure this continues to be the case in the future, so I want to ensure that all of my commands and sub-commands have loaded correctly.

My first thought to do this was to just run

commands = ["config", "othercommand"]
runner = CliRunner()
result = runner.invoke(cli.main)
for command in commands:
    assert command in result.output

This has a few pitfalls, from my point of view.

  • It requires that I update commands each time I add a new one. I'd love to be able to automatically generate this.
  • If I have a command that is a relatively common word that would appear in help test (ie. config), I could get a false result.
  • I don't know how I'd handle sub-commands of each of these in an elegant way.

My application is laid out like this:

@click.group()
def main():
    """My entry point"""

@click.group()
def config():
    """config group"""

@config.command()
def add_config():
    """this is a subcommand to config"""

@click.group()
def othercommand():
    """othercommand group"""

@othercommand.command()
def do_thing():
    """this is a subcommand to othercommand"""

My question: Is there a way to get a list of all commands (and sub-commands) that I can use and do this from my test suite? Preferably without all the surrounding help test so that I can eliminate false results.

Conciliator answered 15/5, 2019 at 13:48 Comment(0)
V
8

It is possible to introspect the cli to get its structure like:

Code:

def command_tree(obj):
    if isinstance(obj, click.Group):
        return {name: command_tree(value)
                for name, value in obj.commands.items()}

Test Code:

import click

@click.group()
def main():
    """My entry point"""

@main.group()
def config():
    """config group"""

@config.command()
def add_config():
    """this is a subcommand to config"""

@main.group()
def othercommand():
    """othercommand group"""


@othercommand.command()
def do_thing():
    """this is a subcommand to othercommand"""


print(command_tree(main))

Results:

{'config': {'add_config': None}, 'othercommand': {'do_thing': None}}
Vanesavanessa answered 15/5, 2019 at 23:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.