在 laravel 中测试外部进程时,避免真正执行命令的核心做法是拦截 process 实例的创建或替换其运行逻辑。具体而言,可以通过 mock 容器中的 illuminate\contracts\process\factory 接口,或者使用 process::fake()(laravel 10+)来预设返回值。这样在调用 run() 方法时,程序不会启动子进程,而是直接返回模拟的 processresult 对象,从而确保测试快速、隔离且无副作用。

如何 Mock Process Factory 以控制输入输出?
通过 Mock ProcessFactory 可以精确控制每个命令的执行结果。开发者需要在测试类中调用 $this->mock(ProcessFactory::class),然后使用 shouldReceive('process(...)->andReturn($result)') 链式调用。当代码中调用 app(Factory::class)->process('ls')->run() 时,框架会返回预设的结果对象,而不会执行实际的 shell 命令。

use Illuminate\Support\Facades\Mockery;
use Illuminate\Contracts\Process\Factory as ProcessFactory;
use Illuminate\Process\PendingProcess;
use Illuminate\Process\ProcessResult;
class ExternalCommandTest extends TestCase
{
public function test_ls_command()
{
// 1. Mock Factory
$factory = Mockery::mock(ProcessFactory::class);
// 2. Define expected result
$result = new ProcessResult([
'exitCode' => 0,
'output' => "file1.txt\nfile2.txt",
'errorOutput' => '',
'command' => 'ls',
'pid' => 12345
]);
// 3. Expectation: process('ls') returns our mock result
$factory->shouldReceive('process')
->with('ls')
->andReturn(new PendingProcess('ls'));
// Note: In newer Laravel, you might mock the run() directly on a pending process instance if needed,
// but typically mocking the factory's process() method to return a specific PendingProcess
// that is then mocked for run() is the granular way.
// Simpler approach for Laravel 10+ is using Process::fake()
}
}
如何使用 Process::fake() 简化测试流程?
Laravel 10 引入了 Process::fake() 静态方法,这是更推荐的测试方式。它允许你注册一个回调函数,该回调接收 PendingProcess 实例并返回一个 ProcessResult。这种方法不需要手动 Mock 容器绑定,代码更简洁。你可以在测试的 setUp 方法或具体测试方法中调用 Process::fake(),然后定义匹配规则。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。

use Illuminate\Support\Facades\Process;
use Illuminate\Process\InvokedProcess;
class BackupServiceTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// Enable fake mode for all processes in this test
Process::fake();
}
public function test_backup_creation()
{
// Arrange: Define what happens when 'tar' is called
Process::fake(function ($process) {
if (str_contains($process->command, 'tar')) {
return new InvokedProcess(
fn() => null, // No real process to close
0 // Exit code
);
}
return new InvokedProcess(fn() => null, 1);
});
// Act: Call your service method which internally uses Process
$service = app(BackupService::class);
$service->createBackup();
// Assert: Verify the process was called with correct arguments
Process::assertExecuted(function ($process) {
return str_contains($process->command, 'tar') &&
$process->arguments['--compress'] === true;
});
}
}
如果命令包含动态参数,Fake 模式如何匹配?
当外部命令的参数是动态生成的(如时间戳、随机 ID),使用固定的 fake() 配置可能会失败。此时应使用 Process::fake() 的闭包形式,并在断言时使用 assertExecutedContaining 或自定义闭包进行模糊匹配。这样可以验证命令的核心部分是否正确,同时忽略动态变化的部分,确保测试的稳定性。










