set_error_handler 可捕获 trigger_error 触发的用户错误(如 e_user_error),需在测试中注册自定义处理器并抛出 exception,再用 phpunit 的 expectexception 捕获;测试后须调用 restore_error_handler 清理,避免污染后续测试。

用 set_error_handler 捕获 trigger_error 并转为异常
PHP 原生错误(如 E_USER_WARNING、E_USER_ERROR)不会触发 try-catch,直接导致脚本中断或静默忽略。测试这类逻辑,必须先用 set_error_handler 把错误“兜住”,再手动抛出异常,才能被 PHPUnit 的 expectException 捕获。
常见错误现象:直接在 try 块里调用 trigger_error('xxx', E_USER_ERROR),结果测试崩溃或无响应,catch 完全不执行。
- 测试前调用
set_error_handler,返回一个闭包,内部throw new ErrorException($errstr, $errno) - 确保该处理器只在当前测试生效,测试结束后立即调用
restore_error_handler() -
ErrorException是标准类,可直接用于$this->expectException(ErrorException::class) - 若需区分错误类型,可在抛出时传入具体
$errno,并在catch中用$e->getSeverity()断言
测试自定义异常类而不是靠 $code 字符串
别试图用 $this->expectExceptionCode('user_not_found')——它只接受整数,PHP 强制类型检查会报错。真正语义化、可测试的方案是为每种业务错误定义独立异常类,比如 UserNotFoundException、InsufficientBalanceException。
这样做的好处不只是“看起来清楚”,而是让 PHPUnit 能精准匹配类型,且 IDE 和静态分析工具能识别继承关系。
- 每个自定义异常类只需继承
\Exception,无需额外逻辑 - 测试时直接写
$this->expectException(UserNotFoundException::class) - 避免在构造函数里塞一堆上下文数组,那会让断言变重、耦合变高
- 如果已有老代码用整数
$code,不要强行改,优先补上对应类名的异常分支
用 Mockery 或 createMock 替换依赖中的错误触发点
当被测方法内部调用了第三方服务、数据库驱动或 SDK,而这些组件可能抛出非标准异常(比如 Guzzle 的 RequestException、PDO 的 PDOException),就不能靠手动 throw 模拟了——得让模拟对象“假装出错”。
常见陷阱:用 willReturn(null) 代替 willThrowException(),结果测试走的是正常路径,根本没覆盖异常分支。
- 用
$mock = $this->createMock(SomeService::class)创建模拟对象 - 调用
$mock->method('fetchData')->willThrowException(new RuntimeException('timeout')) - 再调用
$this->expectException(RuntimeException::class),然后执行被测方法 - 注意:模拟对象抛出的异常类必须和
expectException声明的完全一致(含命名空间),否则匹配失败
CLI 脚本异常终止必须检查 exit 状态码
命令行 PHP 脚本遇到未捕获异常,默认退出码是 255。如果你的脚本设计为“异常即退出”,光看 stdout 输出是否报错远远不够——得验证进程真实退出状态。
容易被忽略的点:在 PHPUnit 中用 exec() 或 proc_open() 运行 CLI 脚本时,不显式获取 $exitCode,就等于没测。
- 用
exec('php mycli.php 2>/dev/null', $output, $exitCode)获取退出码 - 断言
$this->assertSame(255, $exitCode),而不是只检查输出内容 - 若脚本有自定义
register_shutdown_function处理致命错误,也要确保它最终调用exit(1)等明确码值 - 不同 PHP 版本对未捕获异常的默认 exit code 一致(255),但自定义 shutdown handler 可能覆盖它,务必实测
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











