ydhouib's avatar

File upload unkown error with phpunit

Hello,

I have create a test project to upload a file it is working when i use a browser but when i try to execute a test it doesn't.

I have an error when i try to move the file :

Caused by
exception 'Symfony\Component\HttpFoundation\File\Exception\FileException' with message 'The file "photo.JPG" was not uploaded due to an unknown error.' in /Users/xxx/uploader/vendor/symfony/http-foundation/File/UploadedFile.php:251
Stack trace:

Do you have an idea why this fails with php unit ?

Here is the code for the test

  /** @test */
    public function it_can_upload_a_file()
    {

        //given
        $test_file_path = base_path().'/tests/data/photo.JPG';
        $this->assertTrue(file_exists($test_file_path), 'Test file does not exist');
        

        $this->visit('/')
        ->attach($test_file_path,'archive-file')
        ->press('Upload')
        ->see('uploaded successfully');

    }

The code in the controller

  * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        //Get the uploaded file
        $file = $request->file('archive-file');

        //Move it to the final destination
        $directory_path =  public_path() . '/uploaded/';

        $file->move($directory_path);

    }

if i dd the $file in the controller, the file is correctly uploaded :

.Symfony\Component\HttpFoundation\File\UploadedFile {#346
  -test: false
  -originalName: "photo.JPG"
  -mimeType: "application/octet-stream"
  -size: 228530
  -error: 0
}

I'm stuck ...

0 likes
16 replies
sahibalejandro's avatar

I have the exact same problem, is the same if using submitForm(...) or attach(...).

have you solved it?

ydhouib's avatar

The issue is in the UploadedFIle.php (Symfony\Component\HttpFoundation\File)

The method isValid returns false because the call to is_uploaded_file() always return false.

I have checked and the file is correctly uploaded and file_exists return true on the path of the file. I don't know why is_uploaded_file fails

sahibalejandro's avatar

I think is_uploaded_file() fails because the upload is not in the same HTTP request or something like that, also move_uploaded_file() fails for the same reason, that's why UploadedFile uses a $test flag.

The trick here is how to pass true to that flag from Laravel when runnin in testing env? Or maybe there is something wrong with Laravel when make the HTTP request with attachments on testing env?

It's very weird that Laravel offers this methods to attach files and they don't work... and I think it never worked before with the new integration testing.

So... any clues? Don't let this topic die, we need a solution.

luceos's avatar

What is the complete stacktrace of the exception?

ydhouib's avatar

Here is the complete stack

PHPUnit 4.7.6 by Sebastian Bergmann and contributors.

.F

Time: 341 ms, Memory: 14.25Mb

There was 1 failure:

1) ExampleTest::it_can_upload_a_file
A request to [http://uploader.dev] failed. Received status code [500].

/Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:230
/Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:153
/Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:172
/Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:568
/Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php:556
/Users/user/uploader/tests/ExampleTest.php:34

Caused by
exception 'Symfony\Component\HttpFoundation\File\Exception\FileException' with message 'The file "photo.JPG" was not uploaded due to an unknown error.' in /Users/user/uploader/vendor/symfony/http-foundation/File/UploadedFile.php:254
Stack trace:
#0 /Users/user/uploader/app/Http/Controllers/Uploader.php(48): Symfony\Component\HttpFoundation\File\UploadedFile->move('/Users/user/u...')
#1 [internal function]: App\Http\Controllers\Uploader->store(Object(Illuminate\Http\Request))
#2 /Users/user/uploader/bootstrap/cache/compiled.php(8416): call_user_func_array(Array, Array)
#3 /Users/user/uploader/bootstrap/cache/compiled.php(8485): Illuminate\Routing\Controller->callAction('store', Array)
#4 /Users/user/uploader/bootstrap/cache/compiled.php(8465): Illuminate\Routing\ControllerDispatcher->call(Object(App\Http\Controllers\Uploader), Object(Illuminate\Routing\Route), 'store')
#5 [internal function]: Illuminate\Routing\ControllerDispatcher->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#6 /Users/user/uploader/bootstrap/cache/compiled.php(9122): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#7 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#8 /Users/user/uploader/bootstrap/cache/compiled.php(9104): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#9 /Users/user/uploader/bootstrap/cache/compiled.php(8466): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#10 /Users/user/uploader/bootstrap/cache/compiled.php(8451): Illuminate\Routing\ControllerDispatcher->callWithinStack(Object(App\Http\Controllers\Uploader), Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request), 'store')
#11 /Users/user/uploader/bootstrap/cache/compiled.php(7426): Illuminate\Routing\ControllerDispatcher->dispatch(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request), 'App\\Http\\Contro...', 'store')
#12 /Users/user/uploader/bootstrap/cache/compiled.php(7397): Illuminate\Routing\Route->runWithCustomDispatcher(Object(Illuminate\Http\Request))
#13 /Users/user/uploader/bootstrap/cache/compiled.php(7050): Illuminate\Routing\Route->run(Object(Illuminate\Http\Request))
#14 [internal function]: Illuminate\Routing\Router->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#15 /Users/user/uploader/bootstrap/cache/compiled.php(9122): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#16 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#17 /Users/user/uploader/bootstrap/cache/compiled.php(9104): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#18 /Users/user/uploader/bootstrap/cache/compiled.php(7051): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#19 /Users/user/uploader/bootstrap/cache/compiled.php(7039): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#20 /Users/user/uploader/bootstrap/cache/compiled.php(7024): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#21 /Users/user/uploader/bootstrap/cache/compiled.php(2066): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#22 [internal function]: Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http\{closure}(Object(Illuminate\Http\Request))
#23 /Users/user/uploader/bootstrap/cache/compiled.php(9122): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#24 /Users/user/uploader/bootstrap/cache/compiled.php(2686): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#25 [internal function]: Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#26 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#27 /Users/user/uploader/bootstrap/cache/compiled.php(12370): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#28 [internal function]: Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#29 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#30 /Users/user/uploader/bootstrap/cache/compiled.php(11019): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#31 [internal function]: Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#32 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#33 /Users/user/uploader/bootstrap/cache/compiled.php(12108): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#34 [internal function]: Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#35 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#36 /Users/user/uploader/bootstrap/cache/compiled.php(12047): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#37 [internal function]: Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#38 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#39 /Users/user/uploader/bootstrap/cache/compiled.php(2736): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#40 [internal function]: Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#41 /Users/user/uploader/bootstrap/cache/compiled.php(9114): call_user_func_array(Array, Array)
#42 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#43 /Users/user/uploader/bootstrap/cache/compiled.php(9104): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#44 /Users/user/uploader/bootstrap/cache/compiled.php(2013): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#45 /Users/user/uploader/bootstrap/cache/compiled.php(1999): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#46 /Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php(682): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#47 /Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php(151): Illuminate\Foundation\Testing\TestCase->call('POST', 'http://uploader...', Array, Array, Array)
#48 /Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php(172): Illuminate\Foundation\Testing\TestCase->makeRequest('POST', 'http://uploader...', Array, Array, Array)
#49 /Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php(568): Illuminate\Foundation\Testing\TestCase->makeRequestUsingForm(Object(Symfony\Component\DomCrawler\Form))
#50 /Users/user/uploader/vendor/laravel/framework/src/Illuminate/Foundation/Testing/CrawlerTrait.php(556): Illuminate\Foundation\Testing\TestCase->submitForm('Upload', Array)
#51 /Users/user/uploader/tests/ExampleTest.php(34): Illuminate\Foundation\Testing\TestCase->press('Upload')
#52 [internal function]: ExampleTest->it_can_upload_a_file()
#53 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestCase.php(881): ReflectionMethod->invokeArgs(Object(ExampleTest), Array)
#54 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestCase.php(746): PHPUnit_Framework_TestCase->runTest()
#55 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestResult.php(601): PHPUnit_Framework_TestCase->runBare()
#56 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestCase.php(702): PHPUnit_Framework_TestResult->run(Object(ExampleTest))
#57 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestSuite.php(738): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#58 /Users/user/uploader/vendor/phpunit/phpunit/src/Framework/TestSuite.php(738): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult))
#59 /Users/user/uploader/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(428): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult))
#60 /Users/user/uploader/vendor/phpunit/phpunit/src/TextUI/Command.php(147): PHPUnit_TextUI_TestRunner->doRun(Object(PHPUnit_Framework_TestSuite), Array)
#61 /Users/user/uploader/vendor/phpunit/phpunit/src/TextUI/Command.php(99): PHPUnit_TextUI_Command->run(Array, true)
#62 /Users/user/uploader/vendor/phpunit/phpunit/phpunit(36): PHPUnit_TextUI_Command::main()
#63 {main}
FAILURES!
Tests: 2, Assertions: 5, Failures: 1.
luceos's avatar

is debug on? and what is the output of php artisan --version?

luceos's avatar

Seems like Laravel builds the request files through Symfony and Symfony knows nothing about testing. It's very difficult to debug I ran on solid ground when getting to:

vendor/symfony/http-foundation/Request.php:1975

    private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
    {
        if (self::$requestFactory) {
            $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);

            if (!$request instanceof Request) {
                throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
            }

            return $request;
        }

        return new static($query, $request, $attributes, $cookies, $files, $server, $content);
    }
ydhouib's avatar

I'm running on laravel 5.1.6 with debug mode on

 ❯ php artisan --version                                                                                                                                  [12:57:07]
Laravel Framework version 5.1.6 (LTS)

I have reproduced this on a fresh project if you like i can share it.

kevupton's avatar
new UploadedFile($file, 'file',  null, null, null, true);

Solution: Add true to the 6th parameter.

the last parameter indicates whether it is a test file. If this is true, then it will fix your problems, as it skips the validity check.

5 likes
Mehdi_Souihed's avatar

Hi Guys,

I came across a similar problem while trying to test uploading a file on a form. Here is what I have done to make it work :

    
    
// Put this function in a helpers.php or a class (can be called statically) or anywhere you like
function prepareFileUpload($path)
{
    TestCase::assertFileExists($path);

    $finfo = finfo_open(FILEINFO_MIME_TYPE);

    $mime = finfo_file($finfo, $path);

    return new \Symfony\Component\HttpFoundation\File\UploadedFile ($path, null, $mime, null, null, true);
}
        
        //In your test file :
        
        // Fill up your form array
    $form = $this->someFakeData()
        
    $form['file'] = prepareFileUpload('public/upload/file.pdf');
         
    $this->visit($myUrl)->submitForm('Submit', $form);
        
        //... Continue your checks .. see(), assert(), etc..

However if you typically have the following rule in your validator:

   //...
     'file' => 'mimes:pdf',
  //...

You will get an error in these terms : file must be of one these types: pdf

This is because the above rule checks against the mimeType sent by the browser (which is not a terribly good practice) and the PHPUnit crawler always sends a mime type of 'application/octet-stream' and then laravel winges.

So to make the server check itself the mimeType and not rely on the browser, we have to define a custom validator rule:


class AppServiceProvider extends ServiceProvider {

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
          Validator::extend('is_pdf', function($attribute, $value, $parameters, $validator) {

            $mime = \Request::file($attribute)->getMimeType();
            return $mime == 'application/pdf';
        });
    }

      //....

You should also define a custom error message, it is explained in the Laravel Doc (link above).

Then change your rule to :

'file' => 'is_pdf',

This example is hardcoded for the pdf case but I believe it should work for any type of file provided you change your custom validator accordingly.

1 like
mnabialek's avatar

This probably doesn't solve OP problem, but in latest Laravel 5.2 versions make sure you create object of

  \Illuminate\Http\UploadedFile

and not

\Symfony\Component\HttpFoundation\File\UploadedFile

Otherwise you might get quite strange errors as for example with this mimes rule validation failure. There's no point to create any custom validation rules as @Mehdi_Souihed mentioned in his post. For explanation have a look at http://stackoverflow.com/questions/36857800/laravel-5-2-testing-uploadedfile-misses-the-test-value-after-post-bug

1 like

Please or to participate in this conversation.