资讯详情

资讯详情

Flask入门教程(二十九):测试CLI运行器API——CLI命令测试工具

1. 创建测试运行器方式说明app.test_cli_runner(**kwargs)创建CLI测试运行器kwargs传递给CliRunner构造函数with app.test_cli_runner() as runner:上下文管理器可选from app import create_app app create_app() # 标准创建 runner app.test_cli_runner() # 带配置创建如设置环境变量 runner app.test_cli_runner(mix_stderrFalse) # 上下文管理器自动清理 with app.test_cli_runner() as runner: result runner.invoke(args[init-db])2. 核心方法invokeinvoke()是测试运行器的核心方法用于调用CLI命令。参数类型说明cliclick.Command要调用的命令对象默认使用app.cliargslist命令行参数列表不含命令名称如果指定了cli则需包含**kwargsdict传递给ClickCliRunner.invoke()的其他参数如input、env等返回值click.testing.Result包含命令执行结果的对象runner app.test_cli_runner() # 测试内置命令 result runner.invoke(args[routes]) # 等价于执行: flask routes # 测试自定义命令 result runner.invoke(args[init-db]) # 等价于执行: flask init-db3. Result对象属性invoke()返回click.testing.Result对象包含命令执行结果。属性说明exit_code命令退出码0表示成功output命令的标准输出内容strexception如果命令抛出异常此处包含异常对象result runner.invoke(args[routes]) # 验证执行成功 assert result.exit_code 0 # 验证输出内容 assert /api/posts in result.output # 验证无异常 assert result.exception is None4. 测试内置命令# tests/test_cli.py from app import create_app app create_app() runner app.test_cli_runner() def test_routes_command(): 测试flask routes命令 result runner.invoke(args[routes]) assert result.exit_code 0 # 检查输出是否包含注册的路由 assert /api/posts in result.output assert /auth/login in result.output def test_run_help(): 测试flask run --help result runner.invoke(args[run, --help]) assert result.exit_code 0 assert Run a development server in result.output def test_version(): 测试Flask版本信息需要自定义命令或--version # 注意Flask CLI默认没有--version需要自定义 # 或者测试其他内置命令 result runner.invoke(args[routes, --help]) assert result.exit_code 05. 测试自定义命令假设在app.py或蓝图中定义了自定义CLI命令# app.py app.cli.command(init-db) def init_db_command(): 初始化数据库 from db import init_db init_db() print(数据库初始化完成!) app.cli.command(greet) click.argument(name) def greet_command(name): 问候用户 print(fHello, {name}!) app.cli.command(create-user) click.option(--username, -u, requiredTrue) click.option(--email, -e, requiredTrue) def create_user_command(username, email): 创建用户 print(f用户创建成功: {username} ({email}))测试代码# tests/test_cli.py from app import create_app def test_init_db_command(): 测试数据库初始化命令 app create_app() runner app.test_cli_runner() result runner.invoke(args[init-db]) assert result.exit_code 0 assert 数据库初始化完成 in result.output def test_greet_command(): 测试带参数的命令 app create_app() runner app.test_cli_runner() result runner.invoke(args[greet, RUNOOB]) assert result.exit_code 0 assert Hello, RUNOOB! in result.output def test_create_user_with_options(): 测试带选项的命令 app create_app() runner app.test_cli_runner() result runner.invoke(args[ create-user, --username, admin, --email, adminexample.com ]) assert result.exit_code 0 assert 用户创建成功: admin (adminexample.com) in result.output6. 测试命令异常当命令抛出异常时可以通过Result对象的exception属性和exit_code进行验证。# 自定义命令可能抛出异常 app.cli.command(delete-user) click.argument(user_id, typeint) def delete_user_command(user_id): 删除用户 if user_id 1: raise click.ClickException(用户ID必须大于0) print(f用户 {user_id} 已删除)测试异常场景def test_delete_user_invalid_id(): 测试删除用户时传入无效ID app create_app() runner app.test_cli_runner() result runner.invoke(args[delete-user, 0]) # 命令因异常而退出exit_code非0 assert result.exit_code ! 0 # 验证异常信息 assert 用户ID必须大于0 in result.output assert result.exception is not None7. 测试交互式命令输入模拟通过invoke()的input参数可以模拟用户输入# 交互式命令 app.cli.command(confirm-delete) click.argument(username) def confirm_delete_command(username): 确认删除用户 click.echo(f确认删除用户 {username}(y/n)) confirm click.prompt(确认, typebool, defaultFalse) if confirm: click.echo(f用户 {username} 已删除) else: click.echo(操作已取消)测试交互式命令def test_confirm_delete_yes(): 测试交互式命令输入y确认 app create_app() runner app.test_cli_runner() # 模拟输入 y回车 result runner.invoke( args[confirm-delete, runoob], inputy\n ) assert result.exit_code 0 assert 用户 runoob 已删除 in result.output def test_confirm_delete_no(): 测试交互式命令输入n取消 app create_app() runner app.test_cli_runner() result runner.invoke( args[confirm-delete, runoob], inputn\n ) assert result.exit_code 0 assert 操作已取消 in result.output8. 测试环境变量通过invoke()的env参数可以设置环境变量def test_command_with_env_var(): 测试依赖环境变量的命令 app create_app() runner app.test_cli_runner() # 设置环境变量 result runner.invoke( args[some-command], env{FLASK_ENV: production, SECRET_KEY: test-key} ) assert result.exit_code 09. 与pytest集成将CLI测试与pytest集成使用fixture共享测试运行器# tests/conftest.py import pytest from app import create_app pytest.fixture def app(): app create_app() app.config[TESTING] True return app pytest.fixture def runner(app): CLI测试运行器fixture return app.test_cli_runner() # tests/test_cli.py def test_init_db(runner): 测试初始化数据库命令 result runner.invoke(args[init-db]) assert result.exit_code 0 assert 数据库初始化完成 in result.output def test_routes(runner): 测试路由列表命令 result runner.invoke(args[routes]) assert result.exit_code 0 assert /api/posts in result.output10. 完整示例# app.py from flask import Flask import click def create_app(): app Flask(__name__) app.cli.command(hello) click.option(--name, -n, defaultWorld) def hello_command(name): 打印问候语 print(fHello, {name}!) app.cli.command(add) click.argument(a, typeint) click.argument(b, typeint) def add_command(a, b): 计算两数之和 print(f{a} {b} {a b}) app.cli.command(interactive) def interactive_command(): 交互式命令 name click.prompt(请输入你的名字, typestr) age click.prompt(请输入你的年龄, typeint) print(f你好{name}你今年{age}岁。) return app测试文件# tests/test_cli.py import pytest from app import create_app pytest.fixture def runner(): app create_app() return app.test_cli_runner() def test_hello_command(runner): result runner.invoke(args[hello]) assert result.exit_code 0 assert Hello, World! in result.output def test_hello_with_name(runner): result runner.invoke(args[hello, --name, RUNOOB]) assert result.exit_code 0 assert Hello, RUNOOB! in result.output def test_add_command(runner): result runner.invoke(args[add, 5, 3]) assert result.exit_code 0 assert 5 3 8 in result.output def test_interactive_command(runner): result runner.invoke( args[interactive], input小明\n18\n ) assert result.exit_code 0 assert 你好小明你今年18岁。 in result.output def test_routes_command(runner): result runner.invoke(args[routes]) assert result.exit_code 0 # 检查是否有路由信息 assert Endpoint in result.output or Method in result.output def test_unknown_command(runner): result runner.invoke(args[unknown-command]) # 未知命令返回非0退出码 assert result.exit_code ! 0 assert Error in result.output or No such command in result.output11. 测试CLI运行器API速查表类别方法/属性说明创建app.test_cli_runner()创建CLI测试运行器创建app.test_cli_runner(**kwargs)带配置创建调用runner.invoke(args[cmd])调用CLI命令调用runner.invoke(app.cli, [cmd])显式指定CLI组结果result.exit_code退出码0表示成功结果result.output标准输出内容结果result.exception异常对象如有输入模拟invoke(..., inputy\n)模拟标准输入环境变量invoke(..., env{KEY: val})设置环境变量12. 最佳实践实践说明✅每个命令独立测试每个测试函数只测试一个命令的一个场景✅验证exit_code总是检查exit_code判断命令是否成功✅验证输出内容检查output是否包含预期信息✅使用pytest fixture通过fixture共享runner实例减少重复代码✅测试异常场景验证命令在错误输入时的行为✅模拟输入对于交互式命令使用input参数模拟用户输入✅测试环境变量依赖使用env参数设置命令所需的环境变量❌不测试实际数据库操作使用内存数据库或mock避免影响真实数据小结本章全面讲解了Flask测试CLI运行器的完整API。app.test_cli_runner()创建测试运行器invoke(args[cmd])是核心方法返回click.testing.Result对象Result提供exit_code判断命令是否成功、output验证命令输出、exception检查异常可测试内置命令routes、run等和自定义命令通过invoke(..., inputy\n)模拟用户输入测试交互式命令通过invoke(..., env{KEY: val})设置环境变量结合pytest使用fixture管理测试运行器。熟练掌握CLI测试工具有助于确保命令行工具的稳定性和正确性。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →