Laravel Storage Fake测试生成文件无法自动删除问题与解决方案
我在使用Laravel的Storage::fake()时遇到了相关问题,花费了不少时间才调试通代码,不过一开始我还没有完全理解Storage Faker的运行原理。
下方是我的可运行代码精简版:setUp方法会创建Storage模拟对象并生成一个文件,随后将该文件的路径存入数据库。
之后我调用了Repository层的一个函数来校验路径是否存在,该部分代码不便公开,不过这不影响问题说明,因为这部分功能运行正常 ;)
use WithoutMiddleware; use DatabaseTransactions; protected $file; protected function setUp(): void { parent::setUp(); $path = 'protocols'; Storage::fake($path); $this->file = UploadedFile::fake()->createWithContent( 'protocol.html', 'Import gestartet am: 03.09.2019 10:54:13' )->store($path); factory(Protocol::class)->create([ 'partner_id' => 1, 'user_id' => 1, 'path' => $this->file ]); } /** @test */ public function path_exists() { //Called function returns true $path_exists = functionCall('protocols/Test.pdf'); $this->assertTrue($path_exists); $path_exists = functionCall('protocols/Test2.pdf'); $this->assertTrue($path_exists); $path_exists = functionCall('protocols/Test3.pdf'); $this->assertTrue($path_exists); } /** @test */ public function path_does_not_exist() { //Called function throws an Exception $this->expectException(Exception::class); $this->expectExceptionMessage('file_not_found'); $path_exists = functionCall('protocols/XY.pdf'); $this->expectException(Exception::class); $this->expectExceptionMessage('file_not_found'); $path_exists = functionCall('protocols/XY2.pdf'); } protected function tearDown(): void { Storage::delete($this->file); parent::tearDown(); }
到这里运行都正常,但是测试结束后我需要像tearDown方法中写的那样手动删除创建的文件。
这就引出了我的疑问:我看过很多帖子都提到Storage faker不会自动删除创建的文件,为什么会这样呢?既然我用了Storage模拟能力,为什么还需要测试结束后手动删文件?我实在想不通这一点。
还是说我理解有误,Storage Faker本身是可以实现自动删除生成的文件的?
解决方案
我将原本传入Storage::fake()的参数从protocols改成了local。protocols只是local磁盘下的一个路径,本身并不是独立配置的磁盘。
做完这个小改动后,我就可以删掉tearDown方法了,现在测试结束后系统会自动删除生成的文件。
修正后的完整代码:
use WithoutMiddleware; use DatabaseTransactions; protected $file; protected function setUp(): void { parent::setUp(); $path = 'local'; Storage::fake($path); $this->file = UploadedFile::fake()->createWithContent( 'protocol.html', 'Import gestartet am: 03.09.2019 10:54:13' )->store($path); factory(Protocol::class)->create([ 'partner_id' => 1, 'user_id' => 1, 'path' => $this->file ]); } /** @test */ public function path_exists() { //Called function returns true $path_exists = functionCall('protocols/Test.pdf'); $this->assertTrue($path_exists); $path_exists = functionCall('protocols/Test2.pdf'); $this->assertTrue($path_exists); $path_exists = functionCall('protocols/Test3.pdf'); $this->assertTrue($path_exists); } /** @test */ public function path_does_not_exist() { //Called function throws an Exception $this->expectException(Exception::class); $this->expectExceptionMessage('file_not_found'); $path_exists = functionCall('protocols/XY.pdf'); $this->expectException(Exception::class); $this->expectExceptionMessage('file_not_found'); $path_exists = functionCall('protocols/XY2.pdf'); }
原理说明
Storage::fake() 的入参必须是你在 config/filesystems.php 中配置的合法磁盘名称,而非子目录路径。调用该方法时,Laravel会自动将对应磁盘的存储根目录映射到系统临时目录的随机文件夹,PHP测试进程结束后,系统会自动清理临时目录下的所有文件,无需手动删除。
之前传入的protocols只是local磁盘下的子目录,不是独立配置的磁盘,所以fake机制没有生效,文件实际写入了本地真实存储路径,因此需要手动在tearDown中删除。
内容的提问来源于stack exchange,提问作者Nele

