如何在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
相关产品推荐
相关产品推荐

