使用Laravel Excel编写文件上传测试时遇验证错误求助
问题:Laravel测试中上传Excel文件时提示“The file field must be a file.”
我正在使用Spatie Laravel Data和maatwebsite Laravel Excel,测试需要使用tests文件夹内的特定xlsx模板文件,无法用虚拟文件替代。编写文件上传测试时,始终遇到以下错误:
FAIL Tests\Feature\Pages\Settings\Preparation\CreateTest ⨯ it can import an preparation list 0.57s ✓ it can not possible to upload a file with a wrong format 0.38s ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── FAILED Tests\Feature\Pages\Settings\Preparation\CreateTest > it can import an preparation list ValidationException The file field must be a file. at vendor/laravel/framework/src/Illuminate/Support/helpers.php:328 324▕ function throw_if($condition, $exception = 'RuntimeException', ...$parameters) 325▕ { 326▕ if ($condition) { 327▕ if (is_string($exception) && class_exists($exception)) { ➜ 328▕ $exception = new $exception(...$parameters); 329▕ } 330▕ 331▕ throw is_string($exception) ? new RuntimeException($exception) : $exception; 332▕ } +3 vendor frames 4 app/Http/Controllers/PreparationController.php:28 +52 vendor frames 57 tests/Feature/Pages/Settings/Preparation/CreateTest.php:24 Tests: 1 failed, 1 passed (2 assertions) Duration: 1.06s
相关代码
测试代码
it('can import an preparation list', function () { Excel::fake(); $preparation = new UploadedFile( preparationListPath(), 'preparation.xlsx', 'application/vnd.ms-excel.sheet.macroenabled.12', null, true ); $this->withoutExceptionHandling(); asAdmin()->post(action([PreparationController::class, 'store']), [ 'file' => $preparation, ])->assertRedirect(action([PreparationController::class, 'edit'], ['preparation' => 1])); assertDatabaseCount('preparations', 1); })->only();
测试助手函数
function preparationListPath(): string { return base_path('tests/Files/preparation.xlsx'); }
控制器store方法
public function store(CreatePreparationFormRequestData $request): RedirectResponse { $request->validate([ 'file' => 'mimes:xlsx,xls,csv,xlsm', ]); Excel::import(new FirstSheetImport, $request->file); toast('Fichier importé', 'Le fichier a bien été importé.'); return Redirect::action([PreparationController::class, 'edit'], ['preparation' => 1]); }
DTO代码
<?php namespace App\Data; use Illuminate\Http\UploadedFile; use Spatie\LaravelData\Attributes\Validation\File; use Spatie\LaravelData\Data; use Spatie\TypeScriptTransformer\Attributes\TypeScript; #[TypeScript] class CreatePreparationFormRequestData extends Data { public function __construct( #[File] public UploadedFile $file, ) { } }
已尝试的解决方法
- 移除控制器中的验证规则:实际应用中上传正常,但测试仍失败
- 测试中创建
UploadedFile实例并确认类型,但错误依旧 - 移除DTO中的
#[File]验证,触发“function rules can be called staticly”错误
解决方案
问题核心是**UploadedFile构造参数错误以及Laravel Data与原生验证的重复触发**,按以下步骤修复:
修正测试中的
UploadedFile实例
构造UploadedFile时,第四个参数需传入UPLOAD_ERR_OK(表示上传无错误),之前传null会让Laravel判定文件上传异常,触发验证错误。修改代码:$preparation = new UploadedFile( preparationListPath(), 'preparation.xlsx', 'application/vnd.ms-excel.sheet.macroenabled.12', UPLOAD_ERR_OK, true // 标记为测试环境文件 );合并DTO与控制器的验证逻辑
Laravel Data的#[File]属性已自动添加file验证规则,控制器中重复调用$request->validate()会导致文件被二次处理,引发冲突。将验证规则移至DTO,移除控制器中的验证代码:// 修改DTO use Spatie\LaravelData\Attributes\Validation\Mimes; #[TypeScript] class CreatePreparationFormRequestData extends Data { public function __construct( #[File, Mimes(['xlsx', 'xls', 'csv', 'xlsm'])] public UploadedFile $file, ) { } } // 控制器中移除以下代码 // $request->validate([ // 'file' => 'mimes:xlsx,xls,csv,xlsm', // ]);确保请求以正确格式传递
测试中使用post方法时,Laravel会自动处理multipart/form-data格式,但如果仍有问题,可显式指定请求头:asAdmin()->post( action([PreparationController::class, 'store']), ['file' => $preparation], ['Content-Type' => 'multipart/form-data'] )->assertRedirect(action([PreparationController::class, 'edit'], ['preparation' => 1]));
内容的提问来源于stack exchange,提问作者WantyJF
相关产品推荐
相关产品推荐

