You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中如何用Storage::fake在PHPUnit测试中伪造storage_path()?

Laravel测试中伪造storage/history目录的解决方案

问题根源分析

1. Storage::fake(storage_path())报错原因

Storage::fake()的参数是文件系统磁盘的名称(对应config/filesystems.php里的磁盘配置项),不是本地绝对路径。你传入storage_path()后,系统会试图在测试临时目录(storage/framework/testing/disks/)下创建包含完整盘符的子目录(比如C:\xxx\yyy\storage),这违反了操作系统的路径规则,自然报错。

2. Storage::fake('storage')仍删除真实文件的原因

你的业务代码是直接通过storage_path()函数拼接路径,然后用原生PHP函数(比如unlink、glob)操作文件,这种方式会绕过Laravel的Storage facade。而Storage::fake()只对通过Storage facade调用的磁盘操作生效,所以无法拦截原生文件操作,导致真实文件被删除。


解决方案

方案一:改用Storage facade操作(推荐,符合Laravel最佳实践)

步骤1:配置自定义磁盘

在config/filesystems.php的disks数组中添加一个名为history的磁盘:

'disks' => [
    // ... 其他已有磁盘配置
    'history' => [
        'driver' => 'local',
        'root' => storage_path('history'),
    ],
],

步骤2:修改业务代码

将所有直接操作storage/history的代码,替换为通过Storage facade调用history磁盘:

// 示例:写入文件
// 原代码:file_put_contents(storage_path('history/daily_20240520.json'), json_encode($data));
Storage::disk('history')->put('daily_20240520.json', json_encode($data));

// 示例:删除单个文件
// 原代码:unlink(storage_path('history/daily_20240520.json'));
Storage::disk('history')->delete('daily_20240520.json');

// 示例:删除目录下所有文件
// 原代码:array_map('unlink', glob(storage_path('history/*.json')));
Storage::disk('history')->delete(Storage::disk('history')->files());

步骤3:测试中伪造磁盘

在PHPUnit测试方法中,直接伪造history磁盘即可:

use Illuminate\Support\Facades\Storage;

public function test_delete_history_files()
{
    // 伪造history磁盘,所有操作都会指向临时目录
    Storage::fake('history');

    // 先写入测试文件
    Storage::disk('history')->put('test1.json', 'test data');
    Storage::disk('history')->put('test2.json', 'test data');

    // 调用你的删除函数
    $this->callDeleteHistoryFilesFunction();

    // 断言文件已被删除
    Storage::disk('history')->assertMissing('test1.json');
    Storage::disk('history')->assertMissing('test2.json');
}

方案二:模拟storage_path()函数(适合无法修改业务代码的场景)

如果不能修改原有业务代码,可以通过模拟storage_path()辅助函数,将storage/history的路径指向测试临时目录:

use Illuminate\Support\Facades\File;
use Mockery;

public function setUp(): void
{
    parent::setUp();

    // 模拟storage_path函数,替换history路径为测试临时目录
    Mockery::mock('function:storage_path', function ($mock) {
        $mock->shouldReceive('storage_path')->withAnyArgs()->andReturnUsing(function ($path) {
            if ($path === 'history' || str_starts_with($path, 'history/')) {
                $testPath = base_path('tests/Temp/history');
                // 确保测试目录存在
                File::ensureDirectoryExists($testPath);
                return $testPath . '/' . ltrim($path, 'history/');
            }
            // 其他storage_path调用保持原路径
            return storage_path($path);
        });
    });
}

public function tearDown(): void
{
    // 测试结束后清理临时目录
    File::deleteDirectory(base_path('tests/Temp/history'));
    parent::tearDown();
}

内容的提问来源于stack exchange,提问作者Hoyt Jolly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:27:42