Personalizar el navegador globalmente
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 SubscribeNuestra prueba funciona... pero la API nos devuelve JSON, no JSON-LD. ¿Por qué?
Cuando hicimos antes la petición a GET
, no incluimos una cabecera Accept
para indicar qué formato queríamos de vuelta. Pero... JSON-LD es el formato por defecto de nuestra API, así que nos lo devolvió.
Sin embargo, cuando hacemos una petición a ->post()
con la clave json
, se añade una cabeceraContent-Type
establecida en application/json
-lo que está bien-, pero también se añade una cabecera Accept
establecida en application/json
. Sí, le estamos diciendo al servidor que queremos que nos devuelva JSON plano, no JSON-LD.
Quiero utilizar JSON-LD en todas partes. ¿Cómo podemos hacerlo? El segundo argumento de->post()
puede ser una matriz o un objeto llamado HttpOptions
. DigamosHttpOptions::json()
... y luego pasar directamente el array. A ver... si entiendo bien la sintaxis:
// ... lines 1 - 7 | |
use Zenstruck\Browser\HttpOptions; | |
// ... lines 9 - 12 | |
class DragonTreasureResourceTest extends KernelTestCase | |
{ | |
// ... lines 15 - 42 | |
public function testPostToCreateTreasure(): void | |
{ | |
// ... lines 45 - 52 | |
->post('/api/treasures', HttpOptions::json([ | |
'name' => 'A shiny thing', | |
'description' => 'It sparkles when I wave it in the air.', | |
'value' => 1000, | |
'coolFactor' => 5, | |
'owner' => '/api/users/'.$user->getId(), | |
])) | |
// ... lines 60 - 62 | |
; | |
} | |
} |
Hasta aquí, esto es equivalente a lo que teníamos antes. Pero ahora podemos cambiar algunas opciones diciendo ->withHeader()
pasando Accept
y application/ld+json
:
// ... lines 1 - 12 | |
class DragonTreasureResourceTest extends KernelTestCase | |
{ | |
// ... lines 15 - 42 | |
public function testPostToCreateTreasure(): void | |
{ | |
// ... lines 45 - 52 | |
->post('/api/treasures', HttpOptions::json([ | |
'name' => 'A shiny thing', | |
'description' => 'It sparkles when I wave it in the air.', | |
'value' => 1000, | |
'coolFactor' => 5, | |
'owner' => '/api/users/'.$user->getId(), | |
])->withHeader('Accept', 'application/ld+json')) | |
// ... lines 60 - 62 | |
; | |
} | |
} |
También podríamos haberlo hecho con la matriz de opciones: tiene una clave llamadaheaders
. Pero el objeto está muy bien.
Asegurémonos de que esto arregla las cosas. Ejecuta la prueba:
symfony php bin/phpunit --filter=testPostToCreateTreasure
Envío global de la cabecera
Y... ¡volvemos a JSON-LD! Tiene los campos correctos y la respuesta application/ld+json
Encabezado Content-Type
.
Así que .... mola... pero hacer esto cada vez que hacemos una petición a nuestra API en las pruebas es... mega cutre. Necesitamos que esto ocurra automáticamente.
Una buena forma de hacerlo es aprovechar una clase base de prueba. Dentro de tests/
, en realidad dentro de tests/Functional/
, crea una nueva clase PHP llamada ApiTestCase
. Voy a llamarla abstract
y extender KernelTestCase
:
// ... lines 1 - 2 | |
namespace App\Tests\Functional; | |
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; | |
// ... lines 6 - 9 | |
abstract class ApiTestCase extends KernelTestCase | |
{ | |
// ... lines 12 - 25 | |
} |
Dentro, añade el rasgo HasBrowser
. Pero vamos a hacer algo astuto: vamos a importar el método browser()
pero lo llamaremos baseKernelBrowser
:
// ... lines 1 - 7 | |
use Zenstruck\Browser\Test\HasBrowser; | |
abstract class ApiTestCase extends KernelTestCase | |
{ | |
use HasBrowser { | |
browser as baseKernelBrowser; | |
} | |
// ... lines 15 - 25 | |
} |
¿Por qué demonios lo hacemos? Reimplementa el método browser()
... luego llama a $this->baseKernelBrowser()
pasándole $options
y $server
. Pero ahora llama a otro método: ->setDefaultHttpOptions()
. PásaleHttpOptions::create()
y luego ->withHeader()
, Accept
, application/ld+json
:
// ... lines 1 - 5 | |
use Zenstruck\Browser\HttpOptions; | |
// ... lines 7 - 9 | |
abstract class ApiTestCase extends KernelTestCase | |
{ | |
// ... lines 12 - 15 | |
protected function browser(array $options = [], array $server = []) | |
{ | |
return $this->baseKernelBrowser($options, $server) | |
->setDefaultHttpOptions( | |
HttpOptions::create() | |
->withHeader('Accept', 'application/ld+json') | |
) | |
; | |
} | |
} |
¡Listo! De vuelta en nuestra clase de prueba real, extiende ApiTestCase
: coge el que es de nuestra app:
// ... lines 1 - 11 | |
class DragonTreasureResourceTest extends ApiTestCase | |
{ | |
// ... lines 14 - 63 | |
} |
¡Ya está! Cuando decimos $this->browser()
, ahora llama a nuestro métodobrowser()
, que cambia esa opción por defecto. Celébralo eliminandowithHeader()
... y podrías volver a la matriz de opciones con una clavejson
si quieres.
Vamos a probarlo.
symfony php bin/phpunit --filter=testPostToCreateTreasure
Y... uh oh. Es un error extraño:
No se puede anular el método final
_resetBrowserClients()
Esto... es porque estamos importando el trait de la clase padre y de nuestra clase... lo que hace que el trait se vuelva loco. Elimina el que está dentro de nuestra clase de prueba:
// ... lines 1 - 8 | |
use Zenstruck\Browser\Test\HasBrowser; | |
// ... lines 10 - 11 | |
class DragonTreasureResourceTest extends ApiTestCase | |
{ | |
use HasBrowser; | |
// ... lines 15 - 63 | |
} |
ya no lo necesitamos. También haré una pequeña limpieza en mis sentencias use
.
Y ya está:
symfony php bin/phpunit --filter=testPostToCreateTreasure
¡Lo tengo! Volvemos a tener JSON-LD con cero trabajo extra. Elimina ese dump()
:
// ... lines 1 - 11 | |
class DragonTreasureResourceTest extends ApiTestCase | |
{ | |
// ... lines 14 - 41 | |
public function testPostToCreateTreasure(): void | |
{ | |
// ... lines 44 - 45 | |
$this->browser() | |
// ... lines 47 - 59 | |
->dump() | |
// ... line 61 | |
; | |
} | |
} |
A continuación: vamos a escribir otra prueba que utilice nuestro token de autenticación de la API.
I created an abstract
ApiTestCase
class, but while running the tests, I got the following error: