Login to bookmark this video
Buy Access to Course
29.

Pruebas, Parte 1: Twig y Componentes Vivos

|

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

Todos estos ingeniosos artilugios que hemos construido no son más que juguetes, a menos que podamos probarlos. Así que, ¡esa es la misión de hoy! Hay mucho que hacer, así que ¡manos a la obra!

Ejecuta:

composer require phpunit

Que instala el symfony/test-pack, nos da todos los paquetes que necesitamos y los pone en require-dev.

Probar un componente Twig

Para nuestro primer acto, vamos a probar un Componente Twig. Esto está muy bien: podemos crear el objeto componente, llamar a sus métodos y comprobar cómo se representa, todo ello de forma aislada. Es sencillo, pero vamos a probar el componente Button.

En el directorio tests/, crea un directorio Integration/ -porque esto será una prueba de integración- y luego Twig/Components/. Si no conoces las pruebas de integración, consulta nuestro Tutorial de pruebas de integración.

Dentro, crea una nueva clase ButtonTest... y amplía la normal KernelTestCase para pruebas de integración:

// ... lines 1 - 2
namespace App\Tests\Integration\Twig\Components;
// ... lines 4 - 5
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
// ... lines 7 - 8
class ButtonTest extends KernelTestCase
{
// ... lines 11 - 21
}

Para ayudarnos a trabajar con el componente, utiliza un rasgo llamado InteractsWithTwigComponents, y añade una nueva función: testButtonRendersWithVariants():

// ... lines 1 - 6
use Symfony\UX\TwigComponent\Test\InteractsWithTwigComponents;
class ButtonTest extends KernelTestCase
{
use InteractsWithTwigComponents;
public function testButtonRendersWithVariants()
{
// ... lines 15 - 20
}
}

Montar el componente

El trait nos proporciona dos métodos. El primero nos permite crear el objeto componente. Digamos $this->mountTwigComponent() pasando el nombre del componente Button y cualquier props, como variant puesto en success.

Esto debería darnos un Button: assertInstanceOf, Button::class, $component. Vuelca $component y luego assertSame que success es igual a $component->variant:

// ... lines 1 - 8
class ButtonTest extends KernelTestCase
{
// ... lines 11 - 12
public function testButtonRendersWithVariants()
{
$component = $this->mountTwigComponent('Button', [
'variant' => 'success',
]);
dump($component);
$this->assertInstanceOf(Button::class, $component);
$this->assertSame('success', $component->variant);
}
}

¡Genial! Para probarlo, ejecuta:

./vendor/bin/simple-phpunit tests/Integration

Eso descargará PHPUnit, y... ¡pasa! Tenemos algunos avisos de desaprobación, pero ignóralos.

Renderizar el componente

Lo segundo que podemos hacer es renderizar un componente. Copia la parte superior, pégala en la parte inferior, cámbiale el nombre a $rendered y llama a renderTwigComponent(). Esto tiene casi los mismos argumentos, pero también podemos pasar bloques. El tercer argumento es un atajo para pasar el bloque content.

Vuelca $rendered:

// ... lines 1 - 8
class ButtonTest extends KernelTestCase
{
// ... lines 11 - 12
public function testButtonRendersWithVariants()
{
$component = $this->mountTwigComponent('Button', [
'variant' => 'success',
]);
$this->assertInstanceOf(Button::class, $component);
$this->assertSame('success', $component->variant);
$rendered = $this->renderTwigComponent('Button', [
'variant' => 'success',
], '<span>Click me!</span>');
dump($rendered);
}
}

¡Y veamos qué aspecto tiene esto!

./vendor/bin/simple-phpunit tests/Integration

¡Fantástico! Un objeto con el HTML dentro. Con esto, podemos obtener la cadena en bruto... o podemos acceder a un objeto Crawler. Esto es genial: $this->assertSame() queClick Me!, es igual a $rendered->crawler()->filter() - para encontrar el span - entonces->text():

// ... lines 1 - 8
class ButtonTest extends KernelTestCase
{
// ... lines 11 - 12
public function testButtonRendersWithVariants()
{
$component = $this->mountTwigComponent('Button', [
'variant' => 'success',
]);
$this->assertInstanceOf(Button::class, $component);
$this->assertSame('success', $component->variant);
$rendered = $this->renderTwigComponent('Button', [
'variant' => 'success',
], '<span>Click me!</span>');
$this->assertSame('Click me!', $rendered->crawler()->filter('span')->text());
}
}

¡Genial! Mi editor está gritando 'error de sintaxis', pero está siendo dramático. Observa:

./vendor/bin/simple-phpunit tests/Integration

¡Pasa!

Probar un componente vivo

¿Qué tal si probamos la integración de un componente vivo... como nuestro elegante SearchSite? En el mismo directorio, crea una nueva clase llamada SearchSiteTest, extiende KernelTestCase y... esta vez utiliza InteractsWithLiveComponents. Crea un método: testCanRenderAndReload():

// ... lines 1 - 2
namespace App\Tests\Integration\Twig\Components;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\LiveComponent\Test\InteractsWithLiveComponents;
class SearchSiteTest extends KernelTestCase
{
use InteractsWithLiveComponents;
public function testCanRenderAndReload()
{
// ... lines 14 - 15
}
}

Con este trait, podemos decir que $testComponent es igual a $this->createLiveComponent(). Pasar el nombre - SearchSite... y también podemos pasar cualquier props, pero no lo haré. Dejaremos que $query empiece vacío. dd($testComponent):

// ... lines 1 - 5
use Symfony\UX\LiveComponent\Test\InteractsWithLiveComponents;
class SearchSiteTest extends KernelTestCase
{
use InteractsWithLiveComponents;
public function testCanRenderAndReload()
{
$testComponent = $this->createLiveComponent('SearchSite');
dd($testComponent);
}
}

Cuando lo ejecutemos:

./vendor/bin/simple-phpunit tests/Integration

El objeto es enorme... pero es un TestLiveComponent. Y tiene un montón de cosas buenas. Podemos decir $testComponent->component() para obtener el objeto componente subyacente, podemos renderizarlo, e incluso podemos imitar el comportamiento del usuario, como cambiar un valor del modelo, llamar a acciones en vivo, emitir eventos o incluso iniciar sesión.

Configuración de la base de datos de prueba

Para probar la búsqueda, tenemos que añadir algunos viajes a la base de datos. En la parte superior,use ResetDatabase y use Factories:

// ... lines 1 - 7
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
class SearchSiteTest extends KernelTestCase
{
use InteractsWithLiveComponents;
use ResetDatabase;
use Factories;
// ... lines 16 - 26
}

Aquí abajo, utiliza VoyageFactory::createMany() para crear 5 travesías... y dales a todas el mismo purpose para que podamos buscarlas fácilmente. A continuación, crea otroVoyage con cualquier otro purpose aleatorio:

// ... lines 1 - 10
class SearchSiteTest extends KernelTestCase
{
// ... lines 13 - 16
public function testCanRenderAndReload()
{
VoyageFactory::createMany(5, [
'purpose' => 'first 5 voyages',
]);
VoyageFactory::createOne();
$testComponent = $this->createLiveComponent('SearchSite');
dd($testComponent);
}
}

Antes de aprovecharlos, vuelve a hacer la prueba:

./vendor/bin/simple-phpunit tests/Integration

¡Un error de conexión a la base de datos! Estoy ejecutando la base de datos a través de Docker y utilizando el binario symfonypara establecer la variable de entorno DATABASE_URL. Para inyectar esa variable al ejecutar la prueba, antepone al comando symfony php:

symfony php vendor/bin/simple-phpunit tests/Integration

Y... ¡ya estamos de vuelta! Una prueba arriesgada porque no tenemos ninguna aserción. ¡Vamos a añadirlas!

Recuerda: si no hay query, nuestro componente no devuelve ningún viaje. Y en la plantilla templates/components/SearchSite.html.twig, cuando tenemos resultados, cada uno es una etiqueta a.

En la prueba, $this->assertCount() que 0 es igual a$testComponent->render(), luego utiliza ese mismo ->crawler() para filtrar las etiquetas a.

Aquí está la parte realmente guay: llama a $testComponent->set() query a first 5para imitar al usuario escribiendo en el cuadro de búsqueda. Y ahora deberíamos tener 5 resultados:

// ... lines 1 - 10
class SearchSiteTest extends KernelTestCase
{
// ... lines 13 - 16
public function testCanRenderAndReload()
{
// ... lines 19 - 23
$testComponent = $this->createLiveComponent('SearchSite');
$this->assertCount(0, $testComponent->render()->crawler()->filter('a'));
$testComponent->set('query', 'first 5');
$this->assertCount(5, $testComponent->render()->crawler()->filter('a'));
}
}

¡Hazlo!

symfony php vendor/bin/simple-phpunit tests/Integration

¡Verde! Vale, hoy es un día poco ortodoxo porque... se nos ha acabado el tiempo... ¡pero tengo más cosas que decir! La próxima parte será la segunda, en la que nos ocuparemos de las pruebas funcionales de nuestro frontend con JavaScript.