You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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与原生验证的重复触发**,按以下步骤修复:

  1. 修正测试中的UploadedFile实例
    构造UploadedFile时,第四个参数需传入UPLOAD_ERR_OK(表示上传无错误),之前传null会让Laravel判定文件上传异常,触发验证错误。修改代码:

    $preparation = new UploadedFile(
        preparationListPath(),
        'preparation.xlsx',
        'application/vnd.ms-excel.sheet.macroenabled.12',
        UPLOAD_ERR_OK,
        true // 标记为测试环境文件
    );
    
  2. 合并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',
    // ]);
    
  3. 确保请求以正确格式传递
    测试中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:52:18