恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

CodeIgniter 4 命令行(CLI)测试实战:从 MockInputOutput 到流捕获的完整指南

  • 首页
  • 资讯中心
  • /
  • CodeIgniter 4 命令行(CLI)测试实战:从 MockInputOutput 到流捕获的完整指南

相关资讯

什么是A2A,什么是MCP?用TaoToken统一Key跑通多智能体协作 2026/10/12 3:43:56
从零构建轻量级 Python Agent Harness:用 TaoToken 统一 Key 打通工具调用链路 2026/10/12 3:43:56
引擎基础架构的关键决策:分层、主循环与内存管理 2026/10/12 3:43:56

最新资讯

SkiaSharp Issue-Repro 复现结论判定指南:8 种 conclusion 值的选择逻辑与证据要求
Looking Glass IDD 配置指南:模式列表、默认刷新率、渲染偏好与共享内存约束详解
结论标题 + 结构化证据:用 Kun PPT 工具链打造专业级管理汇报演示文稿
Rust闭包捕获与内存管理:长期运行程序的避坑指南
机器学习银行客户细分实战:KMeans聚类与Python数据可视化全流程
叮当小宝CS管理篇:客服日报怎么写?五个字段让主管一眼看懂

今日推荐

Debian新手入门:从部署到日常操作的完整指南
MongoDB复制集扩缩容实战:从rs.add到选主事故复盘
条形码目标检测数据集实战:从YOLOv8训练到部署

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

CodeIgniter 4 命令行(CLI)测试实战:从 MockInputOutput 到流捕获的完整指南

发布时间:2026/10/12 3:43:56
CodeIgniter 4 命令行(CLI)测试实战:从 MockInputOutput 到流捕获的完整指南 后端Web框架【免费下载链接】CodeIgniter4Open Source PHP Framework (originally from EllisLab)项目地址https://gitcode.com/gh_mirrors/co/CodeIgniter4点击查看免费下载导读本文围绕 CodeIgniter 4以下简称 CI4官方测试指南《Testing CLI Commands》见 user_guide_src/source/testing/cli.rst展开系统讲解如何为spark命令及调用CLI::prompt()、CLI::wait()、CLI::input()等交互式代码编写单元测试。你会掌握四种互补的测试方案4.5.0 引入的MockInputOutput模拟用户输入与命令输出、StreamFilterTrait自动捕获 STDOUT/STDERR、CITestStreamFilter手动单测过滤流以及PhpStreamWrapper用流包装器模拟php://stdin。读完即可为仓库中任意 CLI 命令写出可断言、可验证的 PHPUnit 测试。一、为什么需要专门的 CLI 测试工具CLI 命令与普通控制器测试最大的差异在于两点交互输入CLI::prompt()、CLI::wait()、CLI::input()会阻塞等待用户在终端输入测试进程无法“替用户敲键盘”标准输出命令通过fwrite(STDOUT/STDERR)输出内容而 PHPUnit 默认不捕获这些原生流导致断言无从下手。CI4 通过依赖注入 流级拦截两条路径解决上述问题前者把InputOutput实现替换成测试替身MockInputOutput后者用 PHP 流过滤器stream filter与流包装器stream wrapper在底层截获数据。从源码结构看所有方案最终都汇聚到 system/CLI/CLI.php 的同一个入口CLI类持有静态属性$ioCLI::input()、CLI::write()等全部委托给static::$io上的方法如static::$io-input($prefix)、static::$io-fwrite($handle, $string)见 system/CLI/CLI.php#L195-L198 与 system/CLI/CLI.php#L1137-L1140。因此只要替换$io就能在不改动任何业务代码的情况下接管输入与输出。二、首选方案使用 MockInputOutput4.5.02.1 它能做什么MockInputOutput位于 system/Test/Mock/MockInputOutput.php继承自框架的InputOutput基类system/CLI/InputOutput.php在测试执行期间替换真实的InputOutput从而同时捕获用户输入通过setInputs()预设一个输入队列按调用顺序依次弹出命令输出每次CLI::fwrite()的调用都会被记录为输出数组中的一项。当使用MockInputOutput时你不再需要StreamFilterTrait、CITestStreamFilter和PhpStreamWrapper——它内部已经自行挂载/卸载流过滤器与流包装器MockInputOutput::input()中调用CITestStreamFilter::registration()、PhpStreamWrapper::register()等见 system/Test/Mock/MockInputOutput.php#L134-L161并且会妥善保存、恢复外层StreamFilterTrait的过滤器状态两者可安全组合使用。2.2 核心方法方法签名说明setInputs()setInputs(array $inputs): void设置用户输入数组字符串列表input()时按顺序array_shift弹出若队列为空会抛出LogicExceptiongetOutput()getOutput(?int $index null): string不传参返回全部输出拼接串传0或正整数返回对应下标那次CLI::fwrite()的输出传负数-n返回倒数第 n 项下标越界抛InvalidArgumentExceptiongetOutputs()getOutputs(): array返回完整输出数组每一项对应一次CLI::fwrite()调用以上行为与 system/Test/Mock/MockInputOutput.php#L53-L97 的实现一一对应可在源码中直接核对。2.3 与 CLI 类的挂接方式CLI::setInputOutput($io)把一个MockInputOutput或任意InputOutput子类实例注入CLI类CLI::resetInputOutput()将CLI内的实例恢复为真实的new InputOutput()MockInputOutput::setInputs()设定用户输入队列MockInputOutput::getOutput()取出命令输出。setInputOutput与resetInputOutput在 system/CLI/CLI.php#L1175-L1188 实现方法注释明确标注为“Testing purpose only”仅测试用途。2.4 完整示例测试spark db:table官方文档给出的示例是测试spark db:table命令原文见 user_guide_src/source/testing/cli/001.php。该命令会列出数据库表供用户选择先输入非法值a触发校验错误再输入合法索引0完成选择use CodeIgniter\CLI\CLI; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\Mock\MockInputOutput; final class DbTableTest extends CIUnitTestCase { use DatabaseTestTrait; protected $migrateOnce true; public function testDbTable(): void { // Set MockInputOutput to CLI. $io new MockInputOutput(); CLI::setInputOutput($io); // User will input a (invalid value) and 0. $io-setInputs([a, 0]); command(db:table); // Get the whole output string. $output $io-getOutput(); $expected Which table do you want to see? [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: a; $this-assertStringContainsString($expected, $output); $expected Data of Table db_migrations:; $this-assertStringContainsString($expected, $output); // Remove MockInputOutput. CLI::resetInputOutput(); } }要点拆解command(db:table)是 CI4 测试环境提供的全局函数等价于在终端运行spark db:table测试内可直接调用输入队列[a, 0]模拟了“先给非法值、再给合法值”的真实用户行为两次input()依次消费getOutput()返回完整输出用assertStringContainsString()校验提示文案与结果表格均出现在输出中无论断言成败都应在测试末尾或tearDown()中调用CLI::resetInputOutput()避免污染后续测试用例的 CLI 全局状态。仓库中对同一场景的完整测试位于 tests/system/Commands/Database/ShowTableInfoMockIOTest.php它使用正则断言提示文案、校验消息和表格表头还演示了CLI::reset()、NO_COLOR环境变量等测试前置处理MockInputOutput自身的单元测试见 tests/system/Test/Mock/MockInputOutputTest.php。三、不用 MockInputOutput 时的替代方案当不需要模拟交互输入、只需捕获输出时可以退回到基于流过滤器的两条路径。它们由官方文档的 “Testing CLI Output” 一节组织与 MockInputOutput 是“二选一”的关系——文档明确提示使用 MockInputOutput 时无需再使用下述工具。3.1 StreamFilterTrait自动化的输出捕获4.3.0StreamFilterTraitsystem/Test/StreamFilterTrait.php为需要捕获流如 PHP 自带的STDOUT、STDERR的测试提供便捷封装适合“每个测试都需要捕获输出”的场景。使用方法getStreamFilterBuffer()从缓冲区取回捕获的数据resetStreamFilterBuffer()清空缓冲区便于在同一测试内分段断言。namespace Tests; use CodeIgniter\CLI\CLI; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\StreamFilterTrait; final class SomeTest extends CIUnitTestCase { use StreamFilterTrait; public function testSomeOutput(): void { CLI::write(first.); $this-assertSame(\nfirst.\n, $this-getStreamFilterBuffer()); $this-resetStreamFilterBuffer(); CLI::write(second.); $this-assertSame(second.\n, $this-getStreamFilterBuffer()); } }示例原文见 user_guide_src/source/testing/overview/018.php。自动化机制与注意事项StreamFilterTrait自带配置器会被CIUnitTestCase自动调用。其原理来自测试概览文档 user_guide_src/source/testing/overview.rst#L119-L134 描述的 Trait 约定CIUnitTestCase会扫描测试类使用的 trait自动执行命名为setUp{NameOfTrait}()和tearDown{NameOfTrait}()的方法。该 trait 正是通过setUpStreamFilterTrait()挂载输出/错误过滤器、tearDownStreamFilterTrait()卸载过滤器来实现自动化的。因此有一条重要规则如果你在测试中重写了setUp()或tearDown()必须显式调用parent::setUp()与parent::tearDown()否则 trait 的自动配置不会生效过滤器不会被挂载。3.2 CITestStreamFilter手动/单次使用的流过滤器CITestStreamFiltersystem/Test/Filters/CITestStreamFilter.php是php_user_filter的子类仅在需要捕获流的单个测试中手动使用比全局 trait 更克制。API 一览CITestStreamFilter::registration()注册过滤器内部调用stream_filter_register()并清空缓冲区只注册一次CITestStreamFilter::addOutputFilter()把过滤器挂到STDOUTCITestStreamFilter::addErrorFilter()把过滤器挂到STDERRCITestStreamFilter::removeOutputFilter()从STDOUT移除过滤器CITestStreamFilter::removeErrorFilter()从STDERR移除过滤器。用法示例namespace Tests; use CodeIgniter\CLI\CLI; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\Filters\CITestStreamFilter; final class SomeTest extends CIUnitTestCase { public function testSomeOutput(): void { CITestStreamFilter::registration(); CITestStreamFilter::addOutputFilter(); CLI::write(first.); CITestStreamFilter::removeOutputFilter(); } }示例原文见 user_guide_src/source/testing/overview/020.php。底层原理过滤器通过filter($in, $out, $consumed, $closing)回调把流经的数据追加进静态属性$buffer并返回PSFS_PASS_ONsystem/Test/Filters/CITestStreamFilter.php#L54-L62。读取断言时直接访问CITestStreamFilter::$buffer即可。StreamFilterTrait::getStreamFilterBuffer()本质上就是返回这个静态缓冲区可见两者是同一套机制的“自动版”与“手动版”。另外4.6 版本为过滤器补充了hasOutputFilter()/hasErrorFilter()状态查询方法供组合使用如 MockInputOutput 保存外层过滤器状态时判断当前挂载情况。四、测试 CLI 输入PhpStreamWrapper4.1 用途与原理PhpStreamWrappersystem/Test/PhpStreamWrapper.php4.3.0是一个针对php协议的流包装器类专用于编写依赖CLI::prompt()、CLI::wait()、CLI::input()这类用户输入方法的测试。它通过stream_wrapper_unregister(php)注销 PHP 内建包装器再把自己注册到php协议上从而控制php://stdin的读取内容。API 一览PhpStreamWrapper::register()把PhpStreamWrapper注册到php协议PhpStreamWrapper::restore()把php协议恢复为 PHP 内建包装器PhpStreamWrapper::setContent()设置将被“读取”的输入数据。从实现看register()/restore()分别对应stream_wrapper_unregister()stream_wrapper_register()与stream_wrapper_restore()system/Test/PhpStreamWrapper.php#L44-L56stream_read()按position从静态内容$content中分段截取stream_eof()在读完时返回true从而模拟一次完整的标准输入读取system/Test/PhpStreamWrapper.php#L66-L72。4.2 使用示例namespace Tests; use CodeIgniter\CLI\CLI; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\PhpStreamWrapper; final class SomeTest extends CIUnitTestCase { public function testPrompt(): void { // Register the PhpStreamWrapper. PhpStreamWrapper::register(); // Set the user input to red. It will be provided as php://stdin output. $expected red; PhpStreamWrapper::setContent($expected); $output CLI::prompt(What is your favorite color?); $this-assertSame($expected, $output); // Restore php protocol wrapper. PhpStreamWrapper::restore(); } }示例原文见 user_guide_src/source/testing/overview/019.php。重要警告PhpStreamWrapper的设计目标仅限于测试php://stdin。但一旦注册它会接管所有php协议的流包括php://stdout、php://stderr、php://memory等。因此强烈建议只在需要时注册、用完立即恢复restore()否则会干扰其他内建 php 流的行为。这也正是 MockInputOutput 内部采用“用完即恢复”策略先register()再restore()的原因。五、方案选择速查与最佳实践场景推荐工具备注命令需要用户交互输入prompt/wait/input且要断言输出MockInputOutput4.5.0同时解决输入模拟与输出捕获无需其他工具仅需捕获 STDOUT/STDERR测试中普遍需要StreamFilterTrait4.3.0自动挂载/卸载重写setUp/tearDown时必须调用 parent仅单个测试需要捕获输出CITestStreamFilter手动注册、挂载、移除需要为CLI::input()系列方法喂入原始输入数据不使用 MockInputOutputPhpStreamWrapper4.3.0仅拦截php://stdin场景注册期间会接管整个 php 协议用完必须restore()实践建议优先 MockInputOutput。它是 4.5.0 起官方推荐的一站式方案仓库自身的 CLI 测试如 tests/system/Test/Mock/MockInputOutputTest.php已在大量用例中验证它还能与StreamFilterTrait组合而不互相干扰MockInputOutput 内部保存并恢复外层过滤器状态。测试收尾务必恢复状态。在tearDown()中调用CLI::resetInputOutput()与CLI::reset()后者重置 CLI 的段、选项、宽高等内部状态见 system/CLI/CLI.php#L1147-L1158防止全局静态状态在测试间泄漏。断言输出时善用getOutput()的下标语义需要校验某一次特定fwrite时用非负下标需要取最近一次输出时用负数下标需要整体比对时直接用无参形式或getOutputs()数组。测试中合理禁用 ANSI 颜色终端控制序列会干扰输出断言仓库测试通常配合putenv(NO_COLOR1)使用参见 tests/system/Commands/Database/ShowTableInfoMockIOTest.php#L41可参考该做法保持输出可预测。六、相关资源导航官方测试指南本文主题user_guide_src/source/testing/cli.rst测试概览与 Trait 自动化机制user_guide_src/source/testing/overview.rst框架实现system/Test/Mock/MockInputOutput.php输入队列 输出数组system/Test/StreamFilterTrait.php自动捕获 traitsystem/Test/Filters/CITestStreamFilter.php流过滤器本体system/Test/PhpStreamWrapper.phpphp 协议流包装器system/CLI/CLI.phpsetInputOutput/resetInputOutput注入入口system/CLI/InputOutput.php真实输入输出基类仓库测试用例tests/system/Test/Mock/MockInputOutputTest.phptests/system/Commands/Database/ShowTableInfoMockIOTest.php赞分享后端Web框架【免费下载链接】CodeIgniter4Open Source PHP Framework (originally from EllisLab)项目地址https://gitcode.com/gh_mirrors/co/CodeIgniter4点击查看免费下载相关推荐SQLFluff CLI 命令参考从 lint/fix 到 parse/render 的完整命令行实战指南SQLFluff CLI 命令参考从 lint/fix 到 parse/render 的完整命令行实战指南 SQLFluff 是一个模块化 SQL linte代码质量Lint格式化静态分析开发工具Arduino CLI 命令行工具实战指南从问题到解决方案的完整工作流Arduino CLI 命令行工具实战指南从问题到解决方案的完整工作流 如果你正在寻找一种更高效、更自动化的 Arduino 开发方式Arduino CLI开发工具嵌入式Cellpose 4 安装与实战指南从 Cellpose-SAM 模型到 GUI 与命令行的完整工作流Cellpose 4 安装与实战指南从 Cellpose SAM 模型到 GUI 与命令行的完整工作流 本文以仓库 README.md https://lin人工智能深度学习计算机视觉图像处理科研生物信息学上一篇Open Beta首发Atuin Desktop如何彻底改变DevOps团队的自动化协作方式下一篇极速前端开发体验Snowpack的非捆绑式开发模式深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号