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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeVamos 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.
On postToCreateTreasure() test:
login succeeds (status: 204).
post('/api/treasures') returns 401 instead of 422.
While the same testing with /api (swagger) post to /api/treasures returns 422 (the expected).
I do not prefix with 'test' my test methods, I am using the @test keyword in docblock:
`...
/**
There was 1 failure:
1) App\Tests\Functional\DragonTreasureResourceTest::postToCreateTreasure
Current response status code is 401, but 422 expected.