Http Tests

Http Tests

ℹ️ Los ejemplos están en Pest, acceder a la documentación oficial para ver ejemplos en phpunit.

Introduction

Laravel ofrece una API muy fluida para realizar solicitudes HTTP a tu aplicación y examinar las respuestas. Por ejemplo, echa un vistazo a la prueba de características definida a continuación:

<?php
 
test('the application returns a successful response', function () {
    $response = $this->get('/');
 
    $response->assertStatus(200);
});

El método get realiza una solicitud GET a la aplicación, mientras que el método assertStatus afirma que la respuesta devuelta debe tener el código de estado HTTP dado. Además de esta afirmación simple, Laravel también contiene una variedad de afirmaciones para inspeccionar los encabezados de respuesta, el contenido, la estructura JSON y más.

Making Requests

Para realizar una solicitud a tu aplicación, puedes invocar los métodos getpostputpatch o delete dentro de tu prueba. Estos métodos no emiten en realidad una solicitud HTTP “real” a tu aplicación. En su lugar, toda la solicitud de red se simula internamente. En lugar de devolver una instancia de Illuminate\Http\Response, los métodos de solicitud de prueba devuelven una instancia de Illuminate\Testing\TestResponse, que proporciona una variedad de afirmaciones útiles que te permiten inspeccionar las respuestas de tu aplicación:

<?php
 
test('basic request', function () {
    $response = $this->get('/');
 
    $response->assertStatus(200);
});

En general, cada una de tus pruebas solo debería hacer una solicitud a tu aplicación. Pueden ocurrir comportamientos inesperados si se ejecutan múltiples solicitudes dentro de un solo método de prueba.

ℹ️ Para mayor comodidad, el middleware CSRF se desactiva automáticamente al ejecutar pruebas.

Customizing Request Headers

Puedes usar el método withHeaders para personalizar los encabezados de la solicitud antes de que se envíe a la aplicación. Este método te permite agregar cualquier encabezado personalizado que desees a la solicitud:

<?php
 
test('interacting with headers', function () {
    $response = $this->withHeaders([
        'X-Header' => 'Value',
    ])->post('/user', ['name' => 'Sally']);
 
    $response->assertStatus(201);
});

Cookies

Puedes usar los métodos withCookie o withCookies para establecer los valores de las cookies antes de realizar una solicitud. El método withCookie acepta un nombre de cookie y un valor como sus dos argumentos, mientras que el método withCookies acepta un array de pares nombre / valor:

<?php
 
test('interacting with cookies', function () {
    $response = $this->withCookie('color', 'blue')->get('/');
 
    $response = $this->withCookies([
        'color' => 'blue',
        'name' => 'Taylor',
    ])->get('/');
 
    //
});

Sesión / Autenticación

Laravel proporciona varios ayudantes para interactuar con la sesión durante las pruebas HTTP. Primero, puedes establecer los datos de la sesión en un array dado utilizando el método withSession. Esto es útil para cargar la sesión con datos antes de realizar una solicitud a tu aplicación:

<?php
 
test('interacting with the session', function () {
    $response = $this->withSession(['banned' => false])->get('/');
 
    //
});

La sesión de Laravel se utiliza típicamente para mantener el estado del usuario actualmente autenticado. Por lo tanto, el método auxiliar actingAs proporciona una forma sencilla de autenticar a un usuario dado como el usuario actual. Por ejemplo, podemos usar una factoría de modelo para generar y autenticar a un usuario:

<?php
 
use App\Models\User;
 
test('an action that requires authentication', function () {
    $user = User::factory()->create();
 
    $response = $this->actingAs($user)
                     ->withSession(['banned' => false])
                     ->get('/');
 
    //
});

También puedes especificar qué guardia debe usarse para autenticar al usuario dado pasando el nombre del guardia como segundo argumento al método actingAs. La guardia que se proporciona al método actingAs también se convertirá en la guardia predeterminada durante la duración de la prueba:

$this->actingAs($user, 'web')

Debugging Responses

Después de realizar una solicitud de prueba a tu aplicación, se pueden usar los métodos dumpdumpHeaders y dumpSession para examinar y depurar el contenido de la respuesta:

<?php
 
test('basic test', function () {
    $response = $this->get('/');
 
    $response->dumpHeaders();
 
    $response->dumpSession();
 
    $response->dump();
});

Alternativamente, puedes usar los métodos ddddHeaders y ddSession para volcar información sobre la respuesta y luego detener la ejecución:

<?php
 
test('basic test', function () {
    $response = $this->get('/');
 
    $response->ddHeaders();
 
    $response->ddSession();
 
    $response->dd();
});

Exception Handling

A veces es posible que necesites comprobar que tu aplicación está lanzando una excepción específica. Para lograr esto, puedes “simular” el manejador de excepciones a través de la facade Exceptions. Una vez que se haya simulado el manejador de excepciones, puedes utilizar los métodos assertReported y assertNotReported para hacer afirmaciones sobre las excepciones que se lanzaron durante la solicitud:

<?php
 
use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
 
test('exception is thrown', function () {
    Exceptions::fake();
 
    $response = $this->get('/order/1');
 
    // Assert an exception was thrown...
    Exceptions::assertReported(InvalidOrderException::class);
 
    // Assert against the exception...
    Exceptions::assertReported(function (InvalidOrderException $e) {
        return $e->getMessage() === 'The order was invalid.';
    });
});

Los métodos assertNotReported y assertNothingReported se pueden utilizar para afirmar que no se lanzó una excepción dada durante la solicitud o que no se lanzaron excepciones:

Exceptions::assertNotReported(InvalidOrderException::class);
 
Exceptions::assertNothingReported();

Puedes desactivar completamente el manejo de excepciones para una solicitud dada invocando el método withoutExceptionHandling antes de realizar tu solicitud:

$response = $this->withoutExceptionHandling()->get('/');

Además, si deseas asegurarte de que tu aplicación no esté utilizando características que han sido desaprobadas por el lenguaje PHP o las bibliotecas que está utilizando tu aplicación, puedes invocar el método withoutDeprecationHandling antes de realizar tu solicitud. Cuando el manejo de desaprobaciones está desactivado, las advertencias de desaprobación se convertirán en excepciones, lo que provocará que tu prueba falle:

$response = $this->withoutDeprecationHandling()->get('/');

El método assertThrows se puede utilizar para afirmar que el código dentro de una función anónima dada lanza una excepción del tipo especificado:

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

Si deseas inspeccionar y hacer afirmaciones sobre la excepción que se lanza, puedes proporcionar una función anónima como segundo argumento al método assertThrows:

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123;
);

Testing JSON APIs

Laravel también proporciona varios helpers para probar APIs JSON y sus respuestas. Por ejemplo, se pueden usar los métodos jsongetJsonpostJsonputJsonpatchJsondeleteJson y optionsJson para emitir solicitudes JSON con varios verbos HTTP. También puedes pasar fácilmente datos y encabezados a estos métodos. Para empezar, escribamos una prueba para hacer una solicitud POST a /api/user y afirmar que se devolvió los datos JSON esperados:

<?php
 
test('making an api request', function () {
    $response = $this->postJson('/api/user', ['name' => 'Sally']);
 
    $response
        ->assertStatus(201)
        ->assertJson([
            'created' => true,
         ]);
});

Además, los datos de respuesta JSON pueden ser accedidos como variables de array en la respuesta, lo que te permite inspeccionar los valores individuales devueltos dentro de una respuesta JSON:

expect($response['created'])->toBeTrue();

ℹ️ El método assertJson convierte la respuesta a un array para verificar que el array dado existe dentro de la respuesta JSON devuelta por la aplicación. Así que, si hay otras propiedades en la respuesta JSON, esta prueba seguirá siendo válida siempre y cuando el fragmento dado esté presente.

Asserting Exact JSON Matches

Como se mencionó anteriormente, el método assertJson se puede utilizar para afirmar que un fragmento de JSON existe dentro de la respuesta JSON. Si deseas verificar que un array dado coincide exactamente con el JSON devuelto por tu aplicación, debes usar el método assertExactJson:

<?php
 
test('asserting an exact json match', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);
 
    $response
        ->assertStatus(201)
        ->assertExactJson([
            'created' => true,
        ]);
});

Asserting on JSON Paths

Si deseas verificar que la respuesta JSON contenga los datos dados en una ruta especificada, deberías usar el método assertJsonPath:

<?php
 
test('asserting a json path value', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);
 
    $response
        ->assertStatus(201)
        ->assertJsonPath('team.owner.name', 'Darian');
});

El método assertJsonPath también acepta una función anónima, que se puede usar para determinar dinámicamente si la aserción debe pasar:

$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);

Fluent JSON Testing

Laravel también ofrece una manera hermosa de probar de forma fluida las respuestas JSON de tu aplicación. Para comenzar, pasa una función anónima al método assertJson. Esta función anónima se invocará con una instancia de Illuminate\Testing\Fluent\AssertableJson, que se puede usar para hacer afirmaciones contra el JSON que fue devuelto por tu aplicación. El método where se puede usar para hacer afirmaciones contra un atributo particular del JSON, mientras que el método missing se puede usar para afirmar que un atributo particular falta en el JSON:

use Illuminate\Testing\Fluent\AssertableJson;
 
test('fluent json', function () {
    $response = $this->getJson('/users/1');
 
    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                 ->where('name', 'Victoria Faith')
                 ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                 ->whereNot('status', 'pending')
                 ->missing('password')
                 ->etc()
        );
});

Understanding the etc Method

En el ejemplo anterior, es posible que hayas notado que invocamos el método etc al final de nuestra cadena de afirmaciones. Este método informa a Laravel que puede haber otros atributos presentes en el objeto JSON. Si no se utiliza el método etc, la prueba fallará si existen otros atributos contra los que no hiciste afirmaciones en el objeto JSON.

La intención detrás de este comportamiento es protegerte de exponer información sensible en tus respuestas JSON de manera involuntaria, obligándote a realizar una afirmación explícita sobre el atributo o a permitir explícitamente atributos adicionales a través del método etc.

Sin embargo, debes tener en cuenta que no incluir el método etc en tu cadena de aserciones no garantiza que no se estén añadiendo atributos adicionales a los arrays que están anidados dentro de tu objeto JSON. El método etc solo asegura que no existan atributos adicionales en el nivel de anidación en el que se invoca el método etc.

Asserting Attribute Presence / Absence

Para afirmar que un atributo está presente o ausente, puedes usar los métodos has y missing:

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
         ->missing('message')
);

Además, los métodos hasAll y missingAll permiten afirmar la presencia o ausencia de múltiples atributos simultáneamente:

$response->assertJson(fn (AssertableJson $json) =>
    $json->hasAll(['status', 'data'])
         ->missingAll(['message', 'code'])
);

Puedes usar el método hasAny para determinar si al menos uno de una lista dada de atributos está presente:

$response->assertJson(fn (AssertableJson $json) =>
    $json->has('status')
         ->hasAny('data', 'message', 'code')
);

Asserting Against JSON Collections

A menudo, tu ruta devolverá una respuesta JSON que contiene múltiples elementos, como múltiples usuarios:

Route::get('/users', function () {
    return User::all();
});

En estas situaciones, podemos usar el método has del objeto JSON fluente para hacer afirmaciones sobre los usuarios incluidos en la respuesta. Por ejemplo, afirmemos que la respuesta JSON contiene tres usuarios. A continuación, haremos algunas afirmaciones sobre el primer usuario en la colección utilizando el método first. El método first acepta una función anónima que recibe otra cadena JSON afirmable que podemos usar para hacer afirmaciones sobre el primer objeto en la colección JSON:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
             ->first(fn (AssertableJson $json) =>
                $json->where('id', 1)
                     ->where('name', 'Victoria Faith')
                     ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                     ->missing('password')
                     ->etc()
             )
    );

Scoping JSON Collection Assertions

A veces, las rutas de tu aplicación devolverán colecciones JSON que tienen claves con nombre asignadas:

Route::get('/users', function () {
    return [
        'meta' => [...],
        'users' => User::all(),
    ];
})

Al probar estas rutas, puedes usar el método has para afirmar la cantidad de elementos en la colección. Además, puedes usar el método has para limitar una cadena de afirmaciones:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
             ->has('users', 3)
             ->has('users.0', fn (AssertableJson $json) =>
                $json->where('id', 1)
                     ->where('name', 'Victoria Faith')
                     ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                     ->missing('password')
                     ->etc()
             )
    );

Sin embargo, en lugar de hacer dos llamadas separadas al método has para afirmar contra la colección users, puedes hacer una sola llamada que proporcione una función anónima como su tercer parámetro. Al hacerlo, la función anónima se invocará automáticamente y se establecerá en el primer elemento de la colección:

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
             ->has('users', 3, fn (AssertableJson $json) =>
                $json->where('id', 1)
                     ->where('name', 'Victoria Faith')
                     ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                     ->missing('password')
                     ->etc()
             )
    );

Asserting JSON Types

Puede que solo quieras afirmar que las propiedades en la respuesta JSON son de un cierto tipo. La clase Illuminate\Testing\Fluent\AssertableJson proporciona los métodos whereType y whereAllType para hacer precisamente eso:

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('id', 'integer')
         ->whereAllType([
            'users.0.name' => 'string',
            'meta' => 'array'
        ])
);

Puedes especificar múltiples tipos utilizando el carácter |, o pasando un array de tipos como segundo parámetro al método whereType. La afirmación tendrá éxito si el valor de respuesta es cualquiera de los tipos listados:

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('name', 'string|null')
         ->whereType('id', ['string', 'integer'])
);

Los métodos whereType y whereAllType reconocen los siguientes tipos: stringintegerdoublebooleanarray y null.

Testing File Uploads

La clase Illuminate\Http\UploadedFile proporciona un método fake que se puede usar para generar archivos o imágenes de prueba. Esto, combinado con el método fake de la fachada Storage, simplifica en gran medida las pruebas de cargas de archivos. Por ejemplo, puedes combinar estas dos características para probar fácilmente un formulario de carga de avatar:

<?php
 
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
 
test('avatars can be uploaded', function () {
    Storage::fake('avatars');
 
    $file = UploadedFile::fake()->image('avatar.jpg');
 
    $response = $this->post('/avatar', [
        'avatar' => $file,
    ]);
 
    Storage::disk('avatars')->assertExists($file->hashName());
});

Si deseas afirmar que un archivo dado no existe, puedes usar el método assertMissing proporcionado por la fachada Storage:

Storage::fake('avatars');
 
// ...
 
Storage::disk('avatars')->assertMissing('missing.jpg');

Fake File Customization

Al crear archivos utilizando el método fake proporcionado por la clase UploadedFile, puedes especificar el ancho, alto y tamaño de la imagen (en kilobytes) para probar mejor las reglas de validación de tu aplicación:

UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

Además de crear imágenes, puedes crear archivos de cualquier otro tipo utilizando el método create:

UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

Si es necesario, puedes pasar un argumento $mimeType al método para definir explícitamente el tipo MIME que debe devolver el archivo:

UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);

Testing Views

Laravel también te permite renderizar una vista sin realizar una solicitud HTTP simulada a la aplicación. Para lograr esto, puedes llamar al método view dentro de tu prueba. El método view acepta el nombre de la vista y un array opcional de datos. El método devuelve una instancia de Illuminate\Testing\TestView, que ofrece varios métodos para hacer afirmaciones de manera conveniente sobre el contenido de la vista:

<?php
 
test('a welcome view can be rendered', function () {
    $view = $this->view('welcome', ['name' => 'Taylor']);
 
    $view->assertSee('Taylor');
});

La clase TestView proporciona los siguientes métodos de aserción: assertSeeassertSeeInOrderassertSeeTextassertSeeTextInOrderassertDontSee y assertDontSeeText. Si es necesario, puedes obtener el contenido de la vista renderizada en bruto convirtiendo la instancia de TestView a una cadena:

$contents = (string) $this->view('welcome');

Sharing Errors

Algunas vistas pueden depender de errores compartidos en el banco de errores global proporcionado por Laravel. Para llenar el banco de errores con mensajes de error, puedes usar el método withViewErrors:

$view = $this->withViewErrors([
    'name' => ['Please provide a valid name.']
])->view('form');
 
$view->assertSee('Please provide a valid name.');

Rendering Blade and Components

Si es necesario, puedes usar el método blade para evaluar y renderizar una cadena en bruto Blade. Al igual que el método view, el método blade devuelve una instancia de Illuminate\Testing\TestView:

$view = $this->blade(
    '<x-component :name="$name" />',
    ['name' => 'Taylor']
);
 
$view->assertSee('Taylor');

Puedes usar el método component para evaluar y renderizar un componente Blade. El método component devuelve una instancia de Illuminate\Testing\TestComponent:

$view = $this->component(Profile::class, ['name' => 'Taylor']);
 
$view->assertSee('Taylor');

Available Assertions

Response Assertions

La clase Illuminate\Testing\TestResponse de Laravel proporciona una variedad de métodos de aserción personalizados que puedes utilizar al probar tu aplicación. Estas aserciones se pueden acceder en la respuesta que devuelve los métodos de prueba jsongetpostput y delete:

assertAccepted

assertBadRequest

assertConflict

assertCookie

assertCookieExpired

assertCookieNotExpired

assertCookieMissing

assertCreated

assertDontSee

assertDontSeeText

assertDownload

assertJsonMissingValidationErrors

assertJsonPath

assertJsonMissingPath

assertJsonStructure

assertJsonValidationErrors

assertJsonValidationErrorFor

assertLocation

assertExactJson

assertExactJsonStructure

assertForbidden

assertFound

assertGone

assertHeader

assertHeaderMissing

assertInternalServerError

assertJson

assertJsonCount

assertJsonFragment

assertJsonIsArray

assertJsonIsObject

assertJsonMissing

assertJsonMissingExact

assertMethodNotAllowed

assertMovedPermanently

assertContent

assertNoContent

assertStreamedContent

assertNotFound

assertOk

assertPaymentRequired

assertPlainCookie

assertRedirect

assertRedirectContains

assertRedirectToRoute

assertRedirectToSignedRoute

assertRequestTimeout

assertSee

assertUnauthorized

assertUnprocessable

assertUnsupportedMediaType

assertValid

assertInvalid

assertViewHas

assertViewHasAll

assertViewIs

assertSeeInOrder

assertSeeText

assertSeeTextInOrder

assertServerError

assertServiceUnavailable

assertSessionHas

assertSessionHasInput

assertSessionHasAll

assertSessionHasErrors

assertSessionHasErrorsIn

assertSessionHasNoErrors

assertSessionDoesntHaveErrors

assertSessionMissing

assertStatus

assertSuccessful

assertTooManyRequests

assertViewMissing

Authentication Assertions

Laravel también ofrece una variedad de afirmaciones relacionadas con la autenticación que puedes utilizar en las pruebas de características de tu aplicación. Ten en cuenta que estos métodos se invocan en la clase de prueba misma y no en la instancia Illuminate\Testing\TestResponse devuelta por métodos como get y post.

assertAuthenticated

Asegura que un usuario esté autenticado:

**$this->assertAuthenticated($guard = null);**

assertGuest

Asegúrate de que un usuario no esté autenticado:

**$this->assertGuest($guard = null);**

assertAuthenticatedAs

Asegúrate de que un usuario específico esté autenticado:

**$this->assertAuthenticatedAs($user, $guard = null);**

Validation Assertions

Laravel ofrece dos afirmaciones relacionadas con la validación que puedes usar para asegurarte de que los datos proporcionados en tu solicitud eran válidos o inválidos.

assertValid

Asegúrate de que la respuesta no tenga errores de validación para las claves dadas. Este método se puede usar para confirmar respuestas donde los errores de validación se devuelven como una estructura JSON o donde los errores de validación se han almacenado en la sesión:

// Assert that no validation errors are present...
$response->assertValid();
 
// Assert that the given keys do not have validation errors...
$response->assertValid(['name', 'email']);

assertInvalid

Asegúrate de que la respuesta tenga errores de validación para las claves dadas. Este método se puede usar para afirmar contra respuestas donde los errores de validación se devuelven como una estructura JSON o donde los errores de validación se han almacenado en la sesión:

$response->assertInvalid(['name', 'email']);

También puedes afirmar que una clave dada tiene un mensaje de error de validación particular. Al hacerlo, puedes proporcionar el mensaje completo o solo una pequeña porción del mensaje:

$response->assertInvalid([
    'name' => 'The name field is required.',
    'email' => 'valid email address',
]);