Buy Access to Course
02.

Nuestra primera prueba

|

Share this awesome video!

|

Ya tenemos esta clase Dinosaur... y es bastante sencilla. Pero cuando se trata de dinosaurios, los errores en nuestro código pueden ser, mmm, un poco dolorosos. ¡Así que vamos a añadir algunas pruebas básicas!

Crear la clase de prueba

Mmmm... ¿dónde ponemos esta nueva prueba? Técnicamente podemos poner nuestras pruebas en cualquier lugar de nuestro proyecto, pero cuando instalamos symfony/test-pack, Flex creó un directorio tests/ que, no es de extrañar, es el lugar recomendado para poner nuestras pruebas.

Recuerda que, en este tutorial, sólo estamos tratando con pruebas unitarias. Así que, dentro de tests/, crea un nuevo directorio llamado Unit. Y como nuestro Dinosaur::class vive en el espacio de nombres Entity, crea al mismo tiempo un directorio Entitydentro de él.

Toda esta organización es técnicamente opcional: puedes organizar el directorio tests/como quieras. Pero, poner todas nuestras pruebas unitarias en un directorio Unites simplemente... agradable. Y la razón por la que hemos creado el directorio Entityes porque queremos que la estructura de archivos dentro de Unit refleje la estructura de nuestro directorio src/. Es una buena práctica que mantiene nuestras pruebas organizadas.

Por último, crea una nueva clase llamada DinosaurTest. ¡Utilizar ese sufijo Test tiene sentido: estamos probando Dinosaur, así que la llamamos DinosaurTest! Pero también es un requisito: PHPUnit -nuestra biblioteca de pruebas- lo requiere. También requiere que cada clase extienda TestCase:

14 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 2
namespace App\Tests\Unit\Entity;
use PHPUnit\Framework\TestCase;
class DinosaurTest extends TestCase
{
// ... lines 9 - 12
}

Ahora vamos a escribir una prueba sencilla para asegurarnos de que todo funciona.

Dentro de nuestra clase DinosaurTest, añadamos public function testIsWorks()... ¡donde crearemos la prueba más emocionante! Si te gustan los tipos de retorno -¡a mí me gustan! - utilizavoid... aunque eso es opcional

Dentro llama a self::assertEquals(42, 42):

14 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 2
namespace App\Tests\Unit\Entity;
use PHPUnit\Framework\TestCase;
class DinosaurTest extends TestCase
{
public function testItWorks(): void
{
self::assertEquals(42, 42);
}
}

¡Eso es todo! No es una prueba muy interesante - si nuestro ordenador piensa que 42 no es igual a 42, tenemos problemas mayores - pero es suficiente.

Ejecución de PHPUnit

¿Cómo ejecutamos la prueba? Ejecutando PHPUnit. En tu terminal, ejecuta:

./vendor/bin/phpunit

Y... ¡impresionante! PHPUnit vio una prueba -para nuestro único método de prueba- y una aserción.

También podríamos decir bin/phpunit para ejecutar nuestras pruebas, que es básicamente un atajo para ejecutar vendor/bin/phpunit.

Pero, seguro que tienes curiosidad... ¿Qué es... una aserción?

Volviendo a DinosaurTest, la única aserción se refiere al método assertEquals(), que proviene de la clase TestCase de PHPUnit. Si el valor real -42- no coincide con el valor esperado, la prueba fallaría. PHPUnit tiene un montón de métodos de aserción más... y podemos verlos todos yendo a https://phpunit.readthedocs.io. Está lleno de cosas buenas, incluyendo una sección de "Aserciones". Y... ¡vaya! Míralas todas... Hablaremos de las aserciones más importantes a lo largo de la serie. Pero por ahora, ¡volvamos a la prueba!

Convenciones de nomenclatura de las pruebas

Porque, tengo una pregunta: ¿cómo sabe PHPUnit que esto es una prueba? Cuando llamamos avendor/bin/phpunit, PHPUnit hace tres cosas. Primero, busca su archivo de configuración, que es phpunit.xml.dist:

43 lines | phpunit.xml.dist
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
>
<php>
<ini name="display_errors" value="1" />
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5" />
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<!-- Run `composer require symfony/panther` before enabling this extension -->
<!--
<extensions>
<extension class="Symfony\Component\Panther\ServerExtension" />
</extensions>
-->
</phpunit>

Dentro, encuentra testsuites... y la parte directory dice:

43 lines | phpunit.xml.dist
// ... lines 1 - 3
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
// ... lines 5 - 9
>
// ... lines 11 - 19
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
// ... lines 25 - 41
</phpunit>

Eh, PHPUnit: ¡busca pruebas dentro del directorio tests/!

En segundo lugar, encuentra ese directorio y busca recursivamente todas las clases que terminen con la palabraTest. En este caso, DinosaurTest. Por último, una vez que encuentra una clase de prueba, obtiene una lista de todos sus métodos públicos.

Entonces... ¿estoy diciendo que PHPUnit ejecutará cada método público como una prueba? Vamos a averiguarlo! Crea un nuevo public function itWorksTheSame(): void

19 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 6
class DinosaurTest extends TestCase
{
// ... lines 9 - 13
public function itWorksTheSame(): void
{
// ... line 16
}
}

Dentro vamos a self::assertSame() que 42 es igual a 42. assertSame() es muy similar a assertEquals() y veremos la diferencia en un minuto.

19 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 6
class DinosaurTest extends TestCase
{
// ... lines 9 - 13
public function itWorksTheSame(): void
{
self::assertSame(42, 42);
}
}

Ahora, vuelve a tu terminal y ejecutamos de nuevo estas pruebas:

./vendor/bin/phpunit

¿Eh? PHPUnit sigue diciendo que sólo hay una prueba y una aserción. Pero dentro de nuestra clase de prueba, tenemos dos pruebas y dos aserciones. El problema es que PHPUnit sólo ejecuta los métodos públicos que llevan como prefijo la palabra test. Podrías poner la anotación @test encima del método, pero eso no es muy habitual. Así que evitemos ser raros, y cambiemos esto portestItWorksTheSame().

19 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 6
class DinosaurTest extends TestCase
{
// ... lines 9 - 13
public function testItWorksTheSame(): void
{
self::assertSame(42, 42);
}
}

Ahora, cuando ejecutamos la prueba

./vendor/bin/phpunit

¡PHPUnit ve 2 pruebas y 2 aserciones! ¡Shweeeet!

Fallos en las pruebas 😱

¿Qué aspecto tiene cuando falla una prueba? ¡Averigüémoslo! Cambia nuestro 42 esperado por una cadena dentro de testItWorks()... y haz lo mismo dentro de testItWorksTheSame(). Sí, uno de ellos no funciona.

19 lines | tests/Unit/Entity/DinosaurTest.php
// ... lines 1 - 6
class DinosaurTest extends TestCase
{
public function testItWorks(): void
{
self::assertEquals('42', 42);
}
public function testItWorksTheSame(): void
{
self::assertSame('42', 42);
}
}

Esta vez, cuando lo probemos:

./vendor/bin/phpunit

¡Oh, no! ¡Un fallo!

DinosaurTest::testItWorksTheSame() falló al afirmar que 42 es idéntico a 42.

Así que... assertEquals() pasó, pero assertSame() falló. Eso es porqueassertEquals() es el equivalente a hacer un if 42 == 42: utilizando el doble signo de igualdad. Pero assertSame() equivale a 42 === 42: con tres signos iguales.

Y como la cadena 42 no es triplemente igual al entero 42, esa prueba falla y PHPUnit nos grita.

Bien, ¡ya tenemos nuestras primeras pruebas! Aunque... probar que la respuesta a la vida el universo y todo es igual a la respuesta a la vida el universo y todo... no es muy interesante. Así que lo siguiente: vamos a escribir pruebas reales para la clase Dinosaur.