Buy Access to Course
18.

Personalizar el navegador globalmente

|

Share this awesome video!

|

Keep on Learning!

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

Login Subscribe

Nuestra 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+jsonEncabezado 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:

27 lines | tests/Functional/ApiTestCase.php
// ... 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:

27 lines | tests/Functional/ApiTestCase.php
// ... 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:

27 lines | tests/Functional/ApiTestCase.php
// ... 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.