¿Cómo probar la carga de archivos con laravel y phpunit?

6 minutos de lectura

avatar de usuario
jaspe kennis

Estoy tratando de ejecutar esta prueba funcional en mi controlador laravel. Me gustaría probar el procesamiento de imágenes, pero para hacerlo quiero cargar imágenes falsas. ¿Cómo hago esto? Encontré algunos ejemplos en línea, pero ninguno parece funcionar para mí. Esto es lo que tengo:

public function testResizeMethod()
{
    $this->prepareCleanDB();

    $this->_createAccessableCompany();

    $local_file = __DIR__ . '/test-files/large-avatar.jpg';

    $uploadedFile = new Symfony\Component\HttpFoundation\File\UploadedFile(
        $local_file,
        'large-avatar.jpg',
        'image/jpeg',
        null,
        null,
        true
    );


    $values =  array(
        'company_id' => $this->company->id
    );

    $response = $this->action(
        'POST',
        'FileStorageController@store',
        $values,
        ['file' => $uploadedFile]
    );

    $readable_response = $this->getReadableResponseObject($response);
}

Pero el controlador no pasa esta verificación:

elseif (!Input::hasFile('file'))
{
    return Response::error('No file uploaded');
}

Entonces, de alguna manera, el archivo no se pasa correctamente. ¿Cómo hago esto?

  • Puedo ver que has puesto esto en modo de prueba. ¿El archivo cargado es más grande que el tamaño de archivo máximo establecido en php.ini?

    – craig_h

    22 de octubre de 2015 a las 13:06

  • No, eso no es todo, el límite de carga es de 2mb, el archivo de prueba es de 300kb.

    –Jasper Kennis

    22 de octubre de 2015 a las 13:12

avatar de usuario
mikael m

Para cualquier otra persona que se encuentre con esta pregunta, hoy en día puede hacer esto:

    $response = $this->postJson('/product-import', [
        'file' => new \Illuminate\Http\UploadedFile(resource_path('test-files/large-avatar.jpg'), 'large-avatar.jpg', null, null, null, true),
    ]);

ACTUALIZAR

En Laravel 6 el constructor de \Illuminate\Http\UploadedFile La clase tiene 5 parámetros en lugar de 6. Este es el nuevo constructor:

    /**
     * @param string      $path         The full temporary path to the file
     * @param string      $originalName The original file name of the uploaded file
     * @param string|null $mimeType     The type of the file as provided by PHP; null defaults to application/octet-stream
     * @param int|null    $error        The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
     * @param bool        $test         Whether the test mode is active
     *                                  Local files are used in test mode hence the code should not enforce HTTP uploads
     *
     * @throws FileException         If file_uploads is disabled
     * @throws FileNotFoundException If the file does not exist
     */
    public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
    {
        // ...
    }

Entonces la solución anterior se convierte simplemente en:

$response = $this->postJson('/product-import', [
        'file' => new \Illuminate\Http\UploadedFile(resource_path('test-files/large-avatar.jpg'), 'large-avatar.jpg', null, null, true),
    ]);

Esto funciona para mi.

  • Por contribución agrego el constructor. __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) que se llama desde el código anterior. Funciona excelente con imágenes y archivos PDF en mis pruebas unitarias.

    – barfoos

    27 de junio de 2017 a las 5:50

  • ¡Muchas gracias, terminaste una hora de búsqueda infructuosa!

    – Vicente

    20 de septiembre de 2019 a las 4:00

avatar de usuario
Alex Blex

Documentos para CrawlerTrait.html#method_action lee:

Parámetros
cadena $método
cadena $acción
matriz $ comodines
array $parámetros
arreglo $cookies
matriz $archivos
matriz $ servidor
cadena $contenido

Así que asumo que la llamada correcta debería ser

$response = $this->action(
    'POST',
    'FileStorageController@store',
    [],
    $values,
    [],
    ['file' => $uploadedFile]
);

a menos que requiera comodines y cookies que no estén vacíos.

  • Probando tu sugerencia. La jerga de las pruebas me parece muy vaga:p ¿Qué tipo de prueba sería esta?

    –Jasper Kennis

    29 de octubre de 2015 a las 8:54

  • @JasperKennis, no es jerga, sino terminología lo que ayuda a las personas a permanecer en la misma página discutiendo cosas. El mal uso de la terminología confunde y hace lo contrario. Lo que está haciendo es una prueba funcional o de integración, según las intenciones. Como regla general, la prueba unitaria verifica la parte imperativa, es decir, la lógica de una sola clase, burlándose de todo lo demás (razonable). Las pruebas de integración comprueban la parte declarativa, es decir, configuraciones, plantillas, etc. Las pruebas funcionales comprueban la funcionalidad de la aplicación tal como se define en la especificación.

    – Alex Blex

    29 de octubre de 2015 a las 9:36

  • Esto funcionó ^^ La prueba aún falla, pero supera el punto especificado en el que estaba atascado. Tenga en cuenta que estoy usando Laravel 4.2 y eso no toma el $wildcards argumento.

    –Jasper Kennis

    29/10/2015 a las 10:20

  • gracias por esa explicación también, cambié la pregunta para decir prueba funcional.

    –Jasper Kennis

    29 de octubre de 2015 a las 10:23

  • Eso es extraño. $wildcards estuvieron allí desde la primera confirmación de v4.0 github.com/laravel/framework/blob/… No había $cookiesasi que $files eran el quinto parámetro aunque. De todos modos, me alegro de que haya ayudado.

    – Alex Blex

    29/10/2015 a las 10:43

avatar de usuario
Inamur Rahman

La mejor y más fácil manera: Primero importa las cosas necesarias

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

Luego haga un archivo falso para cargar.

Storage::fake('local');
$file = UploadedFile::fake()->create('file.pdf');

Luego haga un JSON Data para pasar el archivo. Ejemplo

$parameters =[
            'institute'=>'Allen Peter Institute',
            'total_marks'=>'100',
            'aggregate_marks'=>'78',
            'percentage'=>'78',
            'year'=>'2002',
            'qualification_document'=>$file,
        ];

Luego envíe los datos a su API.

$user = User::where('email','candidate@fakemail.com')->first();

$response = $this->json('post', 'api/user', $parameters, $this->headers($user));

$response->assertStatus(200);

Espero que funcione.

  • ¿Hay alguna manera de falsificar un archivo PDF real? mi store() valida si en realidad es un PDF

    – bilógico

    21 de septiembre de 2021 a las 2:19

Con phpunit puede adjuntar un archivo a un formulario usando el método de adjuntar().

Ejemplo de lumen documentos:

public function testPhotoCanBeUploaded()
{
    $this->visit('/upload')
         ->name('File Name', 'name')
         ->attach($absolutePathToFile, 'photo')
         ->press('Upload')
         ->see('Upload Successful!');
}

avatar de usuario
Juha Vehnia

Aquí hay un ejemplo completo de cómo probar con archivos personalizados. Necesitaba esto para analizar archivos CSV con formato conocido, por lo que mis archivos tenían que tener el formato y el contenido exactos. Si solo necesita imágenes o archivos de tamaño aleatorio, use los métodos $file->fake->image() o create(). Esos vienen incluidos con Laravel.

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

class PanelistImportTest extends TestCase
{
    /** @test */
    public function user_should_be_able_to_upload_csv_file()
    {
        // If your route requires authenticated user
        $user = Factory('App\User')->create();
        $this->actingAs($user);

        // Fake any disk here
        Storage::fake('local');

        $filePath="/tmp/randomstring.csv";

        // Create file
        file_put_contents($filePath, "HeaderA,HeaderB,HeaderC\n");

        $this->postJson('/upload', [
            'file' => new UploadedFile($filePath,'test.csv', null, null, null, true),
        ])->assertStatus(200);

        Storage::disk('local')->assertExists('test.csv');
    }
}

Aquí está el controlador para ir con él:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function save(Request $request)
    {
        $file = $request->file('file');

        Storage::disk('local')->putFileAs('', $file, $file->getClientOriginalName());

        return response([
            'message' => 'uploaded'
        ], 200);
    }
}

avatar de usuario
Máximo Lanín

Añadir similares setUp() método en su caso de prueba:

protected function setUp()
{
    parent::setUp();

    $_FILES = array(
        'image'    =>  array(
            'name'      =>  'test.jpg',
            'tmp_name'  =>  __DIR__ . '/_files/phpunit-test.jpg',
            'type'      =>  'image/jpeg',
            'size'      =>  499,
            'error'     =>  0
        )
    );
}

Esto falsificará su $_FILES global y permitirá que Laravel piense que hay algo cargado.

¿Ha sido útil esta solución?