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

如何在Laravel单元测试中模拟S3存储失败?报错修复

问题场景

你有一个Laravel服务类MyService,其中uploadFile方法通过Storage::disk('s3')->put上传文件到S3,上传失败时会抛出RuntimeException。编写单元测试testUploadFileFails时,尝试用Storage::fake('s3')结合Mock模拟S3存储失败,却遇到Mockery\Exception\BadMethodCallException,提示Received Mockery_2_Illuminate_Filesystem_FilesystemManager::disk(), but no expectations were specified。

问题原因

Storage::fake('s3')是Laravel提供的测试辅助函数,它会自动创建一个模拟的S3磁盘实例并接管存储系统。此时你手动MockFilesystemManager的disk方法,会和Laravel的测试存储系统产生冲突——Mockery会收到未被预期的disk()调用,从而抛出错误。

正确解决方案

不要混用Storage::fake和手动MockFilesystemManager,直接针对模拟磁盘的底层适配器来Mock失败场景即可。

示例代码

首先是你的MyService类(供参考):

namespace App\Services;

use Illuminate\Support\Facades\Storage;
use RuntimeException;

class MyService
{
    public function uploadFile(string $path, $fileContent)
    {
        $uploadSuccess = Storage::disk('s3')->put($path, $fileContent);

        if (!$uploadSuccess) {
            throw new RuntimeException('文件上传S3失败');
        }

        return $uploadSuccess;
    }
}

然后是修正后的单元测试:

namespace Tests\Unit;

use App\Services\MyService;
use Illuminate\Support\Facades\Storage;
use Mockery;
use RuntimeException;
use Tests\TestCase;

class MyServiceTest extends TestCase
{
    public function testUploadFileFails()
    {
        // 初始化Laravel模拟的S3磁盘
        Storage::fake('s3');

        // 获取模拟磁盘的适配器,Mock其write方法(对应上层的put方法)
        $s3Adapter = Mockery::mock($this->app['filesystem']->disk('s3')->getAdapter());
        // 模拟write方法返回false,触发业务代码中的异常
        $s3Adapter->shouldReceive('write')
            ->once()
            ->andReturn(false);

        // 将Mock后的适配器替换到模拟磁盘中
        $this->app['filesystem']->disk('s3')->setAdapter($s3Adapter);

        // 断言会抛出指定异常
        $this->expectException(RuntimeException::class);
        $this->expectExceptionMessage('文件上传S3失败');

        // 执行测试方法
        $service = new MyService();
        $service->uploadFile('test/demo.txt', '测试内容');
    }
}

如果你的业务逻辑是当put方法抛出异常时才触发自定义异常,也可以让Mock的write方法直接抛出异常:

$s3Adapter->shouldReceive('write')
    ->once()
    ->andThrow(new RuntimeException('S3存储服务异常'));
关键注意点
  • Storage::fake('s3')已经帮你完成了磁盘的模拟,不需要再手动MockFilesystemManager的disk方法。
  • Laravel的Storage::put方法底层调用的是适配器的write方法,所以Mock适配器的write方法是模拟上传失败的正确途径。
  • 避免同时使用Storage::fake和手动Mock存储管理器,否则会引发Mockery的调用冲突错误。

内容的提问来源于stack exchange,提问作者Dimitrios Desyllas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:53:16