Cómo probar la descarga de archivos de Laravel excel

Spread the love

Una de las cosas que mas aplaudo en este paquete es el tiempo que dedico el autor para facilitar el uso de pruebas.

Y en este articulo vamos a dar un introducción en el uso mas común que es la descarga de archivos y como podemos comprobar y validar datos usando este paquete.

Crear ruta y controlador

Antes de comenzar a probar es importante que tengamos un ruta para nuestro ejemplo. Así que en el archivo routes/web.php agregue la siguiente ruta.

Route::get('users/export/{report}', 'UserExportController')->middleware('auth');

Ya que agregaste la ruta, el siguiente paso es crear un controlador, que en este caso sera de tipo invocable ya que no requiero otros métodos.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Exports\UsersExportQuery;
use App\Exports\UsersExportCollection;

class UserExportController extends Controller
{
    private $reports = [
        'collection' => UsersExportCollection::class,
        'query' => UsersExportQuery::class,
    ];

    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
     */
    public function __invoke(Request $request)
    {
        if (array_key_exists($request->report, $this->reports)) {
            return new $this->reports[$request->report];
        }

        return response()->json(['error' => 'Report type not supported']);
    }

El controlador solo va a aceptar dos tipos de reportes, que están definidos en la propiedad $this->reports y mediante el request se determina cual de los dos reportes sera usado, si no se encuentra definido el reporte se regresara una respuesta de error, que puedes ver en la ultima linea.

Con esto definido podemos comenzar a definir los dos reportes y su respectiva prueba.

Para el ejercicio vamos a probar los casos mas generales: es decir usando el método collection y el método query de Laravel Excel, así que tendremos dos reportes que ya definimos en la propiedad reports de nuestro controlador.

private $reports = [
        'collection' => UsersExportCollection::class,
        'query' => UsersExportQuery::class,
    ];

Definir reporte mediante método collection de laravel excel

El reporte de usuario mediante colecciones de nuestro ejemplo tiene el siguiente código.

namespace App\Exports;

use App\User;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromCollection;

class UsersExportCollection implements FromCollection, Responsable
{
    use Exportable;

    private $fileName = null;

    public function __construct()
    {
        $this->setFileName();
    }

    /**
     * @return Collection
     */
    public function collection()
    {
        return User::take(10)->get();
    }

    /**
     * Set file name
     *
     * @return void
     */
    public function setFileName() : void
    {
        $this->fileName = sprintf('users-export-%s.xlsx', now()->timestamp);
    }
}

Nuestro reporte es muy sencillo, solo obtienes 10 usuario de la base de datos y el nombre del archivo se genera mediante un timestamp.

Como se prueba.

Esta parte es sumamente sencilla, solo necesitamos llamar al método assertDownloaded.

/** @test */
    public function user_can_download_users_export_collection() : void
    {
        $users = User::take(10)->get();

        $this->actingAs($users->first())
            ->getJson('/users/export/collection');

        Excel::assertDownloaded($this->getFileName(), function(UsersExportCollection $export) use($users) {
            // Assert that the correct export is downloaded.
            return $export->collection()->contains($users->random());
        });
    }

Lo primero que hace nuestra prueba es recuperar 10 usuarios de nuestra base de datos. Uno de los usuarios se utiliza para pasar la autentificación y luego solicitar la generación de un reporte de tipo collection.

Después de hacer la petición, el assertDownload comprueba que el archivo se descarga y que contiene alguno de los 10 elementos que realice en la consulta.

return $export->collection()->contains($users->random());

Es importante destacar que para acceder a los datos del reporte para este caso, tenemos que llamar al método collection como se muestra en el código.

La llamada a $this->getFileName() se encarga de generar un nombre que debe coincidir con el que genera el reporte.

public function getFileName(): string
    {
        return 'users-export-'. now()->timestamp . '.xlsx';
    }

Es probable que te preguntes, como vamos a hacer coincidir el nombre del archivo si estamos usando una función que varia con el tiempo.

Y la respuesta es sencilla; la función now() usa la clase Carbon y este paquete tiene un método interesante que te permite predefinir un timestamp que sera siempre el mismo sin importar cuantas veces llamo a la función now().

Carbon::setTestNow(Carbon::create(2001, 5, 21, 12));

Esta llamada se hace en el método setUp de nuestra prueba.

Con esto ya puedes pasar a ver el siguiente reporte

Definir reporte mediante método query de Laravel Excel

El código para el siguiente reporte es el siguiente:

namespace App\Exports;

use App\User;
use Illuminate\Contracts\Support\Responsable;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;

/**
 * Class UsersExportQuery
 *
 * @package App\Exports
 */
class UsersExportQuery implements FromQuery, Responsable
{
    use Exportable;

    /** @var null */
    private $fileName = null;


    public function __construct()
    {
        $this->setFileName();
    }

    /**
    * @return \Illuminate\Database\Query\Builder
    */
    public function query()
    {
        return User::query()->take(10);
    }

    /**
     * Set file name
     *
     * @return void
     */
    public function setFileName() : void
    {
        $this->fileName = sprintf('users-export-%s.xlsx', now()->timestamp);
    }
}

Como puedes observar el reporte hace exactamente lo mismo, la diferencia es que en este caso se implementa la interfaz FromQuery. Con lo cual Laravel Excel sabe que va a utilizar una instancia del query builder de Laravel.

Cómo se prueba.

Para probar el reporte hacemos lo mismo que en el anterior.

/** @test */
    public function user_can_download_users_export_query() : void
    {
        $users = User::take(10)->get();

        $this->actingAs($users->first())
            ->getJson('/users/export/query');

        Excel::assertDownloaded($this->getFileName(), function(UsersExportQuery $export) use($users) {
            // Assert that the correct export is downloaded.
            return $export->query()->get()->contains($users->random());
        });
    }

Lo único que tiene de diferente este ejemplo del anterior, es como se determina el contenido del archivo de descarga.

return $export->query()->get()->contains($users->random());

Como puedes darte cuenta, se tiene que llamar a get() para recuperar los resultados y comprobar en la colección que existe uno de los elementos de nuestra consulta.

Conclusión

Hacer pruebas con el paquete Laravel Excel es realmente sencillo y solo vimos el caso mas común que es la descarga de archivos, pero también puedes probar cuando guardas en disco entre otras cosas, así que es importante que leas la documentación para conocer mas sobre las pruebas que puedes realizar con este paquete.

Si tienes dudas recuerda dejar tus comentarios y si eres de los que te gusta probar por tu cuenta te dejo el repositorio con el ejemplo completo de este articulo.