Laravel测试环境模拟多Storage时遇Mockery调用异常问题
Laravel测试环境模拟多Storage组件报错问题
问题说明
在Laravel测试环境中模拟多个Storage组件时触发错误,报错发生在调用Storage::fake('public')的位置,错误提示为Mockery\Exception\BadMethodCallException: Received Mockery_2_Illuminate_Filesystem_FilesystemManager::createLocalDriver(), but no expectations were specified。
业务代码
public function sftp(Sibling $sibling) { $file_paths = Storage::build($sibling->config) ->files($this->track->track_token); Storage::disk('public') ->makeDirectory($this->track->track_token); foreach ($file_paths as $file_path) { $file_content = Storage::build($sibling->config) ->get($file_path); TrackMp3::query() ->where('track_id', $this->track->id) ->where('file_name', basename($file_path)) ->update([ 'downloaded_at' => now(), ]); Storage::disk('public') ->put($file_path, $file_content); } }
测试用例代码
public function test_sftp_works_when_track_exists_in_sibling() { $track_token = md5('sample'); $track_320 = UploadedFile::fake() ->create('track_320.mp3') ->getContent(); $track_160 = UploadedFile::fake() ->create('track_160.mp3') ->getContent(); $track_96 = UploadedFile::fake() ->create('track_96.mp3') ->getContent(); $track_demo = UploadedFile::fake() ->create('track_demo.mp3') ->getContent(); $sibling = SiblingFactory::new() ->create(); $public_disk = Storage::fake('public'); $sibling_disk = Storage::fake('sibling'); Storage::shouldReceive('build') ->with($sibling->config) ->andReturn($sibling_disk); $sibling_disk->put($track_token . '/track_320.mp3', $track_320); $sibling_disk->put($track_token . '/track_160.mp3', $track_160); $sibling_disk->put($track_token . '/track_96.mp3', $track_96); $sibling_disk->put($track_token . '/track_demo.mp3', $track_demo); $track = TrackFactory::new() ->md5Fetched() ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_320.mp3'))]) ->fileName320()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_160.mp3'))]) ->fileName160()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_96.mp3'))]) ->fileName96()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_demo.mp3'))]) ->fileNameDemo()) ->create(['track_token' => $track_token]); Storage::fake('public'); // ----> 错误发生在此处 Artisan::call('download'); }
错误信息
Mockery\Exception\BadMethodCallException: Received Mockery_2_Illuminate_Filesystem_FilesystemManager::createLocalDriver(), but no expectations were specified C:\Development\projects\track-download-manager\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php:353 C:\Development\projects\track-download-manager\vendor\laravel\framework\src\Illuminate\Support\Facades\Storage.php:107 C:\Development\projects\track-download-manager\tests\Feature\DownloadCommandTest.php:55
解决方案
问题根源是Storage门面被Mock接管后,再次调用Storage::fake()会触发未被Mock覆盖的方法调用:
- 测试开头已经通过
$public_disk = Storage::fake('public');创建了public磁盘的fake实例,无需重复调用Storage::fake('public');,直接使用已创建的$public_disk即可。 - 当使用
Storage::shouldReceive('build')给Storage设置Mock期望后,Storage门面已被Mockery代理,此时调用fake()会尝试调用createLocalDriver(),但该方法未被设置Mock期望,导致报错。
调整后的测试代码只需删除重复的Storage::fake('public');行即可:
public function test_sftp_works_when_track_exists_in_sibling() { $track_token = md5('sample'); $track_320 = UploadedFile::fake() ->create('track_320.mp3') ->getContent(); $track_160 = UploadedFile::fake() ->create('track_160.mp3') ->getContent(); $track_96 = UploadedFile::fake() ->create('track_96.mp3') ->getContent(); $track_demo = UploadedFile::fake() ->create('track_demo.mp3') ->getContent(); $sibling = SiblingFactory::new() ->create(); $public_disk = Storage::fake('public'); $sibling_disk = Storage::fake('sibling'); Storage::shouldReceive('build') ->with($sibling->config) ->andReturn($sibling_disk); $sibling_disk->put($track_token . '/track_320.mp3', $track_320); $sibling_disk->put($track_token . '/track_160.mp3', $track_160); $sibling_disk->put($track_token . '/track_96.mp3', $track_96); $sibling_disk->put($track_token . '/track_demo.mp3', $track_demo); $track = TrackFactory::new() ->md5Fetched() ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_320.mp3'))]) ->fileName320()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_160.mp3'))]) ->fileName160()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_96.mp3'))]) ->fileName96()) ->has(TrackMp3Factory::new(['md5' => md5_file($sibling_disk->path($track_token . '/track_demo.mp3'))]) ->fileNameDemo()) ->create(['track_token' => $track_token]); // 移除重复的Storage::fake('public') Artisan::call('download'); }
内容的提问来源于stack exchange,提问作者Erfan Sabouri
相关产品推荐
相关产品推荐

