excellaravelmaatwebsite-excelphp-pestlaravel-data

Create upload file test with Laravel Excel


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

Thank you in advance for your help

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'

Solution

  • 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.