Laravel Factory 中需用 Intervention\Image 动态生成真实 JPG/PNG 文件并存至 storage/app/testing/,返回绝对路径字符串;须提前创建目录、避免命名冲突、控制分辨率与质量,并在 tearDown() 中调用 File::cleanDirectory() 清理。

Factory 中如何生成真实图片文件而非占位图链接
直接用 fake()->imageUrl() 只返回 URL 字符串,测试时无法验证图片上传、压缩、存储逻辑。必须让 Factory 生成真实 .jpg 或 .png 文件并保存到本地临时路径(如 storage/app/testing/),再返回该文件路径供模型使用。
推荐用 Intervention\Image 动态生成:
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;
$manager = new ImageManager(new Driver());
$image = $manager->create(800, 600, '#f0f0f0')
->text('Test ' . uniqid(), 50, 50, function ($font) {
$font->size(24)->color('#333');
});
$filePath = storage_path('app/testing/') . 'test_' . time() . '_' . uniqid() . '.jpg';
$image->save($filePath);
return $filePath;
注意:storage_path('app/testing/') 需提前创建并确保可写;Laravel 10+ 默认不自动创建子目录,建议在 setUp() 中加 File::ensureDirectoryExists(storage_path('app/testing'));
模型字段类型为 `string` 但需存文件路径时的 Factory 写法
很多项目把图片存为路径字符串(如 avatar_path 字段),而非直接存二进制。此时 Factory 不应返回 UploadedFile 实例,而应返回绝对路径字符串,并确保该路径在测试运行期间有效、可被 Storage::get() 或 file_get_contents() 正确读取。
- 路径必须是
storage_path()下的相对路径(如testing/avatar_abc123.jpg)或完整绝对路径,避免用public_path()—— 测试环境通常不启用 Web 服务,HTTP 访问不可靠 - 不要用
Storage::putFile()返回 public URL,那只是访问地址,不是实际文件路径 - 生成后建议用
assertFileExists($path)在测试中校验,防止路径拼错或权限问题
批量生成时如何避免磁盘爆满和文件名冲突
跑 1000 条测试数据时,每条都生成新图片,容易撑爆 tmp 目录或 CI 环境磁盘。关键控制点有三个:
- 统一用
testing/子目录,测试结束前清空:File::cleanDirectory(storage_path('app/testing'));放在tearDown()或beforeEach() - 文件名必须含唯一标识:
uniqid()+microtime(true)组合比单纯Str::uuid()更防碰撞(尤其高并发测试) - 降低分辨率与质量:用
$image->scale(400, null)->encode('jpg', 70),单图控制在 50KB 以内,千张约 50MB,可控
测试结束后如何安全清理图片而不影响其他测试
不能依赖 PHP 垃圾回收自动删文件 —— Laravel 测试进程常复用,文件残留会导致后续测试读到旧图、断言失败。
最稳妥方式是在 TestCase 基类中统一管理:
protected function tearDown(): void
{
parent::tearDown();
File::cleanDirectory(storage_path('app/testing'));
}
如果某些测试需保留图片用于调试,可加开关:if (! $_ENV['KEEP_TEST_ASSETS'] ?? false) { ... },配合 phpunit.xml 的 env 配置。
别忘了:Storage driver 为 local 时,Storage::deleteDirectory('testing') 和 File::cleanDirectory() 效果一致,但后者更底层、不依赖 Flysystem,失败率更低。


















