跳转到主要内容
包调用 ffmpeg 或 git 等外部命令时,使用方应用的环境差异和失败会直接变成包的行为。不要直接调用 shell_exec() 或 exec(),而是通过 Process facade 执行,并在测试中用 Process::fake() 替换。
本页面向 Laravel 13,确认了 Framework v13.42.0 的实现。前提是已了解进程与包的测试。这里介绍的数组命令匹配,以 Process 内部使用的 Symfony Process 的参数转换为前提。

避免直接执行的理由

直接调用函数无法在测试中替换,超时和退出码的处理也会因调用方而各不相同。通过 Process facade,可以统一执行记录、fake、超时以及失败时的异常。

以数组传递命令

字符串命令会经过 sh 解析,如果包含用户输入,就会产生引号问题和注入的可能。在包内部请以数组传递参数。
PendingProcess 的默认超时为 60 秒,空闲超时为 null。耗时较长的转换请用 timeout() 明确指定;对于输出可能停滞的处理,请同时使用 idleTimeout()。

失败时的异常与消息

当退出码不表示成功时,ProcessResult::throw() 会抛出 ProcessFailedException。消息中包含所执行的命令、退出码,以及标准输出和标准错误输出。
如果把机密信息放进命令参数,它会被包含在 ProcessFailedException 的消息中,可能出现在日志或页面上。不要把令牌等作为参数传递,而是通过 env() 传递,或写入命令的配置文件。
超时由 ProcessTimedOutException 表示,空闲超时由 ProcessIdleTimedOutException 表示。请向包的使用者明确说明会抛出哪些异常,以及捕获时可以期待什么。

在测试中 fake

在测试中用 Process::fake() 替换外部命令。Process::result() 可以指定标准输出、标准错误和退出码。
向 assertRan() 传入闭包,可以确认 PendingProcess 的 command、timeout、environment 等。数组命令会与 $process->command 进行完全一致比较,因此参数顺序或值的变化会在测试中被检测出来。

数组命令的 fake 键

Process 内部使用 Str::is() 将 fake() 的键与 getCommandline() 的结果进行匹配。数组命令会被转换为面向 shell 的带引号字符串,因此 'ffmpeg*' 这样的字符串模式不会匹配。在数组命令的测试中,请使用以下方式之一。

防止意外的外部命令

要检测测试中不应执行的外部命令,请使用 preventStrayProcesses()。与 fake 不匹配的命令会抛出 RuntimeException,并不会实际启动。

使用注入的 Factory 时

如果包的构造函数接收 Illuminate\Process\Factory,请确认 Process::fake() 的状态能否到达注入目标。Facade 会缓存已解析的实例,swap() 会替换容器中的实例。另一方面,Factory 是否在容器中共享,无法依赖框架的显式注册,因此请在测试中实际确认。 如果无法到达注入目标,请在测试中显式替换 Factory,或将设计改为经由 Process facade。无论哪种情况,用 assertRan() 确认调用已被记录,都能发现遗漏的替换。

为长期维护追加的验证

参考的一手资料

最后修改于 2026年11月13日