Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Tiskiel's avatar

Create upload file test with laravel excel

Hi everyone,

I use Spatie Laravel Data and maatwebsite Laravel Excel.

My test need use specific xlsx file stored inside my tests folder. I've seen a lot of test used fake files but I can't use one because it's special template.

I try to create test to upload file with Laravel Excel. But I always this error :


   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

My test :

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();

Tests Helpers :

function preparationListPath(): string
{
    return base_path('tests/Files/preparation.xlsx');
}

Controller store function :

 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,
    ) {
    }
}

What I tried :

  1. Remove validtion rules. Work when I upload file to my application to real situation. If I keep its, unwork.
  2. To my test I make new instance of UploadFile, I tried DD it and it file type. But same if I remove or keep validation to my controller, the errors stay.
  3. I tried to remove validation rules to my DTO but one error is occured : function rules can be called staticly'

Thank you in advance for your help

0 likes
3 replies
jaseofspades88's avatar

I can't use one because it's special template

The uploaded file fake doesn't care how special your template is, if you're testing the endpoint and you're faking the export, it shouldn't matter how special the template is.

Why are you accepting an uploaded file in your form request constructor? If you're passing an uploaded file to your endpoint, it would be passed within $this->file('file');

1 like
Tiskiel's avatar

@jaseofspades88 I understand, I use instance of UploadedFile because I doesn't found how passing my file. I'm sure I'm doing it wrong. Because, after assertImported, I need to check if it processing work.

Tiskiel's avatar

We've fixed it and split it in to two tests:

First to check if file are imported :

it('can import an preparation list', function () {
    Excel::fake();
    Storage::fake();

    $file = UploadedFile::fake()->create(
        base_path('tests/Files/preparation.xlsm'),
        'preparation.xlsm'
    );

    asAdmin()
        ->post(action([PreparationController::class, 'store']), [
            'file' => $file,
        ], [])
        ->assertRedirect(action([PreparationController::class, 'edit'], ['preparation' => 1]));

    Excel::assertImported('preparation.xlsm');
});

Second to check if all data are correctly imported :

test('a preparation is correctly imported', function () {
    Excel::import(new PreparationImport,
        base_path('tests/Files/preparation.xlsm'));

    assertDatabaseCount('preparations', 1);
    assertDatabaseHas('preparations', [
        'order_number' => '22202959',
        'plan' => '22302959-DWG-070',
        'project' => '',
    ]);
});

Like that both works.

Make attention to one thing when you use Laravel Excel, I had an error when file passed in to my logical and it returned SQL error. This one was fixed with coalesce.

My code before :

public function model(array $row): Preparation
    {

        return new Preparation([
            'order_number' => $row['order_number'],
            'plan' => $row['plan'],
            'project' => $row['project'],
        ]);
    }

My code now:

public function model(array $row): Preparation
    {

        return new Preparation([
            'order_number' => $row['order_number'] ?? '',
            'plan' => $row['plan'] ?? '',
            'project' => $row['project'] ?? '',
        ]);
    }

Just need return empty string in case $row are null.

Please or to participate in this conversation.