Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Probar la autenticación

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Vamos a crear una prueba para publicar y crear un nuevo tesoro. Digamospublic function testPostToCreateTreasure() que devuelve void. Y empezamos igual que antes: $this->browser()->post('/api/treasures'):

... lines 1 - 10
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 13 - 40
public function testPostToCreateTreasure(): void
{
$this->browser()
->post('/api/treasures', [
... line 45
])
... lines 47 - 48
;
}
}

En este caso necesitamos enviar datos. El segundo argumento de cualquiera de estos métodospost() o get() es una matriz de opciones, que puede incluir parámetros headers,query u otras cosas. Una clave es json, que puedes establecer en una matriz, que se codificará en JSON para ti. Empieza enviando JSON vacío... y luego->assertStatus(422). Para ver cómo es la respuesta, añade ->dump():

... lines 1 - 10
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 13 - 40
public function testPostToCreateTreasure(): void
{
$this->browser()
->post('/api/treasures', [
'json' => [],
])
->assertStatus(422)
->dump()
;
}
}

¡Impresionante! Copia el nombre del método de prueba. Quiero centrarme sólo en esta prueba. Para ello, ejecuta:

symfony php bin/phpunit --filter=testPostToCreateTreasure

Y... ¡oh! El código de estado de respuesta actual es 401, pero se esperaba 422.

Volcado de respuestas fallidas en el navegador

Cuando una prueba falla con el navegador, guarda automáticamente la última respuesta en un archivo... lo cual es genial. De hecho, está en el directorio var/. En mi terminal, puedo mantener pulsado Command y hacer clic para abrirlo en el navegador. Eso está muy bien. Me verás hacer esto un montón de veces.

Vale, esto devuelve un código de estado 401. Por supuesto: ¡la ruta requiere autenticación! Nuestra aplicación tiene dos formas de autenticarse: mediante el formulario de acceso y la sesión o mediante un token de API. Vamos a probar ambas, empezando por el formulario de inicio de sesión.

Iniciar sesión durante la prueba

Para iniciar sesión como usuario... ese usuario primero tiene que existir en la base de datos. Recuerda: al inicio de cada prueba, nuestra base de datos está vacía. Nuestro trabajo consiste en llenarla con lo que necesitemos.

Crea un usuario con UserFactory::createOne(['password' => 'pass']) para que sepamos cuál será la contraseña. A continuación, antes de hacer la petición POST para crear un tesoro, ->post() a /login y envía json con email ajustado a$user->getEmail() -para utilizar cualquier dirección de correo electrónico aleatoria que Faker haya elegido- y luego password ajustado a pass. Para asegurarnos de que ha funcionado, ->assertStatus(204):

... lines 1 - 5
use App\Factory\UserFactory;
... lines 7 - 11
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 14 - 41
public function testPostToCreateTreasure(): void
{
$user = UserFactory::createOne(['password' => 'pass']);
$this->browser()
->post('/login', [
'json' => [
'email' => $user->getEmail(),
'password' => 'pass',
],
])
->assertStatus(204)
... lines 54 - 58
;
}
}

Ese es el código de estado que devolvemos tras una autenticación correcta.

¡Vamos a probarlo! Muévete y ejecuta la prueba:

symfony php bin/phpunit --filter=testPostToCreateTreasure

¡Pasa! ¡Obtenemos el código de estado 422 y vemos los mensajes de validación!

Atajo para iniciar sesión: actingAs()

Así que... iniciar sesión es... ¡así de fácil! Y te recomiendo que hagas una prueba que envíe un POST específico a tu ruta de inicio de sesión, como acabamos de hacer, para asegurarte de que funciona correctamente.

Sin embargo, en el resto de mis pruebas... cuando simplemente necesito autenticarme para hacer el trabajo real, hay una forma más rápida de iniciar sesión. En lugar de hacer la petición POST, digamos ->actingAs($user):

... lines 1 - 11
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 14 - 41
public function testPostToCreateTreasure(): void
{
... lines 44 - 45
$this->browser()
->actingAs($user)
... lines 48 - 52
;
}
}

Esta es una forma astuta de tomar el objeto User e introducirlo directamente en el sistema de seguridad de Symfony sin hacer ninguna petición. Es más fácil y más rápido. Y ahora, no nos importa en absoluto cuál es la contraseña, así que podemos simplificarlo.

Vamos a comprobarlo:

symfony php bin/phpunit --filter=testPostToCreateTreasure

¡Sigue bien!

Comprobando el éxito de la creación del tesoro

Hagamos otro POST aquí abajo. Sigue encadenando y añade ->post(). En realidad... me da pereza. Copia el ->post() existente... y úsalo. Pero esta vez, envía datos reales: Voy a teclear rápidamente algunos... estos pueden ser cualquier cosa. La última clave que necesitamos es owner. Ahora mismo, estamos obligados a enviar el owner cuando creamos un tesoro. Pronto lo haremos opcional: si no lo enviamos, lo hará por defecto quien esté autentificado. Pero por ahora, ponlo en /api/users/y luego en $user->getId(). Termina con assertStatus(201):

... lines 1 - 11
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 14 - 41
public function testPostToCreateTreasure(): void
{
$user = UserFactory::createOne();
$this->browser()
->actingAs($user)
->post('/api/treasures', [
'json' => [],
])
->assertStatus(422)
->post('/api/treasures', [
'json' => [
'name' => 'A shiny thing',
'description' => 'It sparkles when I wave it in the air.',
'value' => 1000,
'coolFactor' => 5,
'owner' => '/api/users/'.$user->getId(),
],
])
->assertStatus(201)
;
}
}

Porque 201 es lo que devuelve la API cuando se crea un objeto.

Muy bien, a probar:

symfony php bin/phpunit --filter=testPostToCreateTreasure

¡Sigue pasando! ¡Estamos en racha! Añade un ->dump() para ayudarnos a depurar y luego una comprobación de cordura: ->assertJsonMatches() que name es A shiny thing:

... lines 1 - 11
class DragonTreasureResourceTest extends KernelTestCase
{
... lines 14 - 41
public function testPostToCreateTreasure(): void
{
... lines 44 - 45
$this->browser()
... lines 47 - 60
->assertStatus(201)
->dump()
->assertJsonMatches('name', 'A shiny thing')
;
}
}

Cuando lo probemos

symfony php bin/phpunit --filter=testPostToCreateTreasure

Enviando la cabecera Accept: application/ld+json

Ninguna sorpresa: todo verde. Pero mira la respuesta volcada: ¡no es JSON-LD! Nos devuelve JSON estándar. Puedes verlo en la cabecera Content-Type: 'application/json', no application/ld+json, que es lo que esperaba.

Averigüemos qué está pasando y solucionémoslo globalmente personalizando el funcionamiento del Navegador en todo nuestro conjunto de pruebas.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "api-platform/core": "^3.0", // v3.1.2
        "doctrine/annotations": "^2.0", // 2.0.1
        "doctrine/doctrine-bundle": "^2.8", // 2.8.3
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.14", // 2.14.1
        "nelmio/cors-bundle": "^2.2", // 2.2.0
        "nesbot/carbon": "^2.64", // 2.66.0
        "phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
        "phpstan/phpdoc-parser": "^1.15", // 1.16.1
        "symfony/asset": "6.2.*", // v6.2.5
        "symfony/console": "6.2.*", // v6.2.5
        "symfony/dotenv": "6.2.*", // v6.2.5
        "symfony/expression-language": "6.2.*", // v6.2.5
        "symfony/flex": "^2", // v2.2.4
        "symfony/framework-bundle": "6.2.*", // v6.2.5
        "symfony/property-access": "6.2.*", // v6.2.5
        "symfony/property-info": "6.2.*", // v6.2.5
        "symfony/runtime": "6.2.*", // v6.2.5
        "symfony/security-bundle": "6.2.*", // v6.2.6
        "symfony/serializer": "6.2.*", // v6.2.5
        "symfony/twig-bundle": "6.2.*", // v6.2.5
        "symfony/ux-react": "^2.6", // v2.7.1
        "symfony/ux-vue": "^2.7", // v2.7.1
        "symfony/validator": "6.2.*", // v6.2.5
        "symfony/webpack-encore-bundle": "^1.16", // v1.16.1
        "symfony/yaml": "6.2.*" // v6.2.5
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "mtdowling/jmespath.php": "^2.6", // 2.6.1
        "phpunit/phpunit": "^9.5", // 9.6.3
        "symfony/browser-kit": "6.2.*", // v6.2.5
        "symfony/css-selector": "6.2.*", // v6.2.5
        "symfony/debug-bundle": "6.2.*", // v6.2.5
        "symfony/maker-bundle": "^1.48", // v1.48.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/phpunit-bridge": "^6.2", // v6.2.5
        "symfony/stopwatch": "6.2.*", // v6.2.5
        "symfony/web-profiler-bundle": "6.2.*", // v6.2.5
        "zenstruck/browser": "^1.2", // v1.2.0
        "zenstruck/foundry": "^1.26" // v1.28.0
    }
}
userVoice