Login to bookmark this video
Buy Access to Course
12.

La burla: Objetos simulados

|

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

Nuestras pruebas están pasando, los dinos se pasean, ¡y la vida es genial! Pero... pensemos en esto un segundo. En GithubService, cuando probamos getHealthReport(), podemos controlar el $response que nos devuelve request() mediante un stub. Eso está muy bien, pero también estaría bien asegurarse de que el servicio sólo llama a GitHub una vez y que utiliza el método HTTP correcto con la URL correcta. ¿Podemos hacerlo? Por supuesto

Esperar que se llame a un método

En GithubServiceTest donde configuramos el $mockHttpClient, añadimos ->expects(), y pasamos self::once().

61 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 11
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... lines 19 - 36
$mockHttpClient
->expects(self::once())
// ... lines 39 - 40
;
// ... lines 42 - 45
}
// ... lines 47 - 59
}

En el terminal, ejecuta nuestras pruebas...

./vendor/bin/phpunit

Esperar argumentos específicos

Y... ¡Impresionante! Acabamos de añadir una aserción a nuestro cliente simulado que requiere que el métodorequest se llame exactamente una vez. Vayamos un paso más allá y añadamos ->with() pasando GET... y luego pegaré la URL de la API de GitHub.

62 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 11
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... lines 19 - 36
$mockHttpClient
->expects(self::once())
->method('request')
->with('GET', 'https://api.github.com/repos/SymfonyCasts/dino-park')
->willReturn($mockResponse)
;
// ... lines 43 - 46
}
// ... lines 48 - 60
}

Vuelve a probar las pruebas...

./vendor/bin/phpunit

Y... ¡Huh! Tenemos 2 fallos:

Fallo al afirmar que dos cadenas son iguales

Hmm... ¡Ah Ha! Mis habilidades para copiar y pegar son un poco débiles. Me faltó /issue al final de la URL. Añade eso

62 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 11
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... lines 19 - 36
$mockHttpClient
// ... lines 38 - 39
->with('GET', 'https://api.github.com/repos/SymfonyCasts/dino-park/issues')
// ... line 41
;
// ... lines 43 - 46
}
// ... lines 48 - 60
}

Veamos si ese era el truco:

./vendor/bin/phpunit

Umm... ¡Sí! Estamos en verde todo el día. Pero lo mejor de todo es que las pruebas confirman que estamos utilizando la URL y el método HTTP correctos cuando llamamos a GitHub.

Pero... ¿Y si quisiéramos llamar a GitHub más de una vez? O... ¿queríamos afirmar que no se ha llamado en absoluto? PHPUnit nos tiene cubiertos. Hay un puñado de otros métodos que podemos llamar. Por ejemplo, cambia once() por never().

Y observa lo que ocurre ahora:

./vendor/bin/phpunit

Hmm... Sí, tenemos dos fallos porque

no se esperaba llamar a request().

¡Eso es realmente ingenioso! Vuelve a cambiar el expects() a once() y, para asegurarnos de que no hemos roto nada, vuelve a ejecutar las pruebas.

./vendor/bin/phpunit

Y... ¡Impresionante!

Aplicando cuidadosamente las aserciones

Podríamos llamar a expects() en nuestro $mockResponse para asegurarnos de que toArray()se llama exactamente una vez en nuestro servicio. Pero, ¿realmente nos importa? Si no se llama en absoluto, nuestra prueba fallaría sin duda. Y si se llama dos veces, ¡no pasa nada! Utilizar ->expects() y ->with() son formas estupendas de añadir afirmaciones adicionales... cuando las necesites. Pero no es necesario microgestionar cuántas veces se llama a algo o sus argumentos si eso no es tan importante.

Utilizar GitHubService en nuestra aplicación

Ahora que GithubService está totalmente probado, ¡podemos celebrarlo utilizándolo para manejar nuestro panel de control! En MainController::index(), añade un argumento GithubService $github para autoconectar el nuevo servicio.

33 lines | src/Controller/MainController.php
// ... lines 1 - 5
use App\Service\GithubService;
// ... lines 7 - 10
class MainController extends AbstractController
{
#[Route(path: '/', name: 'main_controller', methods: ['GET'])]
public function index(GithubService $github): Response
{
// ... lines 16 - 30
}
}

A continuación, justo debajo de la matriz $dinos, foreach() sobre $dinos as $dino y, dentro digamos de $dino->setHealth() pasando por $github->getHealthReport($dino->getName()).

33 lines | src/Controller/MainController.php
// ... lines 1 - 5
use App\Service\GithubService;
// ... lines 7 - 10
class MainController extends AbstractController
{
#[Route(path: '/', name: 'main_controller', methods: ['GET'])]
public function index(GithubService $github): Response
{
// ... lines 16 - 23
foreach ($dinos as $dino) {
$dino->setHealth($github->getHealthReport($dino->getName()));
}
// ... lines 27 - 30
}
}

Al navegador y actualiza...

Y... ¿Qué?

getDinoStatusFromLabels() debe ser HealthStatus, null devuelto

¿Qué está pasando aquí? Por cierto, el hecho de que nuestra prueba unitaria pase pero nuestra página falle puede ocurrir a veces y, en un futuro tutorial, escribiremos una prueba funcional para asegurarnos de que esta página realmente se carga.

El error no es muy evidente, pero creo que uno de nuestros dinos tiene una etiqueta de estado que desconocemos. Volvamos a echar un vistazo a los problemas en GitHub y... ¡HA! "Dennis" vuelve a causar problemas. Al parecer, está un poco hambriento...

En nuestro enum HealthStatus, no tenemos un caso para las etiquetas de estado Hungry. Imagínate. ¿Es un dinosaurio hambriento que acepta visitas? No lo sé, supongo que depende de si le preguntas al visitante o al dino. En cualquier caso, Hungry no es un estado que esperemos. Así que, a continuación, vamos a lanzar una excepción clara si nos encontramos con un estado desconocido y a probar esa excepción.