测试Click应用程序

Click提供了{ref}“Click.testing”模块来帮助您调用命令行应用程序并检查其行为。

这些工具应该只用于测试,因为为了简单起见,它们改变了整个解释器的状态。它们不是线程安全的!

实施例使用 [pytest] (https://docs.pytest.org/en/stable/)风格测试。

基本示例

关键部分是:

  • CliRunner -用于以命令行脚本的形式调用命令。

  • Result -从CliRunner.invoke()返回。捕获输出数据、退出代码、可选异常,并将输出捕获为字节和二进制数据。

hello.py
import click

@click.command()
@click.argument('name')
def hello(name):
   click.echo(f'Hello {name}!')
test_hello.py
from click.testing import CliRunner
from hello import hello

def test_hello_world():
  runner = CliRunner()
  result = runner.invoke(hello, ['Peter'])
  assert result.exit_code == 0
  assert result.output == 'Hello Peter!\n'

子命令

A subcommand name must be specified in the args parameter CliRunner.invoke():

sync.py
import click

@click.group()
@click.option('--debug/--no-debug', default=False)
def cli(debug):
   click.echo(f"Debug mode is {'on' if debug else 'off'}")

@cli.command()
def sync():
   click.echo('Syncing')
test_sync.py
from click.testing import CliRunner
from sync import cli

def test_sync():
  runner = CliRunner()
  result = runner.invoke(cli, ['--debug', 'sync'])
  assert result.exit_code == 0
  assert 'Debug mode is on' in result.output
  assert 'Syncing' in result.output

场境设置

传递给{meth}' CliRunner.invoke '的其他关键字参数将用于构造初始{class}'上下文对象'<click.Context>。例如,将固定终端宽度设置为60:

sync.py
import click

@click.group()
def cli():
   pass

@cli.command()
def sync():
   click.echo('Syncing')
test_sync.py
from click.testing import CliRunner
from sync import cli

def test_sync():
  runner = CliRunner()
  result = runner.invoke(cli, ['sync'], terminal_width=60)
  assert result.exit_code == 0
  assert 'Debug mode is on' in result.output
  assert 'Syncing' in result.output

文件系统隔离

{meth}' CliRunner.isolated_filesystem '上下文管理器将当前工作目录设置为新的空文件夹。

cat.py
import click

@click.command()
@click.argument('f', type=click.File())
def cat(f):
   click.echo(f.read())
test_cat.py
from click.testing import CliRunner
from cat import cat

def test_cat():
   runner = CliRunner()
   with runner.isolated_filesystem():
      with open('hello.txt', 'w') as f:
          f.write('Hello World!')

      result = runner.invoke(cat, ['hello.txt'])
      assert result.exit_code == 0
      assert result.output == 'Hello World!\n'

传递路径以控制临时目录的创建位置。在这种情况下,Click不会删除该目录。与Pytest等管理临时文件的框架集成很有用。

test_cat.py
from click.testing import CliRunner
from cat import cat

def test_cat_with_path_specified():
   runner = CliRunner()
   with runner.isolated_filesystem('~/test_folder'):
      with open('hello.txt', 'w') as f:
          f.write('Hello World!')

      result = runner.invoke(cat, ['hello.txt'])
      assert result.exit_code == 0
      assert result.output == 'Hello World!\n'

输入流

测试包装器可以为输入流(stdin)提供输入数据。这对于测试提示非常有用。

prompt.py
import click

@click.command()
@click.option('--foo', prompt=True)
def prompt(foo):
   click.echo(f"foo={foo}")
test_prompt.py
from click.testing import CliRunner
from prompt import prompt

def test_prompts():
   runner = CliRunner()
   result = runner.invoke(prompt, input='wau wau\n')
   assert not result.exception
   assert result.output == 'Foo: wau wau\nfoo=wau wau\n'

将模拟数据流,因此它们也将输入数据写入输出流。如果需要隐藏输入,则不会发生这种情况。