Login to bookmark this video
Buy Access to Course
11.

Mocking: Stubs

|

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

Echemos un vistazo rápido a GithubService para ver exactamente lo que hace. En primer lugar, el constructor requiere un objeto HttpClientInterface que utilizamos para llamar a GitHub. A cambio, obtenemos un ResponseInterface que contiene una matriz de incidencias del repositorio dino-park. A continuación, llamamos al método toArray() en la respuesta, e iteramos sobre cada incidencia para ver si el título contiene el$dinosaurName, de modo que podamos obtener su etiqueta de estado.

57 lines | src/Service/GithubService.php
// ... lines 1 - 8
class GithubService
{
// ... lines 11 - 14
public function getHealthReport(string $dinosaurName): HealthStatus
{
// ... lines 17 - 18
$response = $this->httpClient->request(
method: 'GET',
url: 'https://api.github.com/repos/SymfonyCasts/dino-park/issues'
);
// ... lines 23 - 28
foreach ($response->toArray() as $issue) {
// ... lines 30 - 32
}
// ... lines 34 - 35
}
// ... lines 37 - 55
}

Para que nuestras pruebas pasen, tenemos que enseñar a nuestro falso httpClient que cuando llamemos al método request(), debe devolver un objeto ResponseInterface que contenga datos que nosotros controlamos. Así que... vamos a hacerlo.

Enseñar al Mock qué debe devolver

Justo después de $mockHttpClient, di $mockResponse = $this->createMock() utilizandoResponseInterface::class para el nombre de la clase. Abajo en $mockHttpClient, llama,->method('request') que willReturn($mockResponse). Esto le dice a nuestro cliente simulado que cada vez que llamemos al método request() de nuestro simulado, debe devolver este $mockResponse.

46 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 9
use Symfony\Contracts\HttpClient\ResponseInterface;
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... line 19
$mockHttpClient = $this->createMock(HttpClientInterface::class);
$mockResponse = $this->createMock(ResponseInterface::class);
$mockHttpClient
->method('request')
->willReturn($mockResponse)
;
// ... lines 27 - 30
}
// ... lines 32 - 44
}

Ahora podríamos ejecutar nuestras pruebas, pero fallarían. Hemos enseñado a nuestro cliente simulado lo que debe devolver cuando llamemos al método request(). Pero ahora tenemos que enseñar a nuestro $mockResponse lo que debe hacer cuando llamemos al método toArray(). Así que justo encima, vamos a enseñarle al $mockResponse que cuando llamemos,method('toArray') y él willReturn() un array de incidencias. Porque eso es lo que devuelve GitHub cuando llamamos a la API.

51 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 9
use Symfony\Contracts\HttpClient\ResponseInterface;
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... line 19
$mockHttpClient = $this->createMock(HttpClientInterface::class);
$mockResponse = $this->createMock(ResponseInterface::class);
$mockResponse
->method('toArray')
->willReturn([])
;
$mockHttpClient
->method('request')
->willReturn($mockResponse)
;
// ... lines 32 - 35
}
// ... lines 37 - 49
}

Para cada incidencia, GitHub nos da el "título" de la incidencia y, entre otras cosas, una matriz de "etiquetas". Así que imitemos a GitHub y hagamos que esta matriz incluya una incidencia que tenga 'title' => 'Daisy'.

Y, para la prueba, haremos como si se hubiera torcido el tobillo, así que añadiremos un conjunto de claves labels a un array, que incluya 'name' => 'Status: Sick'.

Vamos a crear también un dino sano para poder afirmar que nuestro análisis sintáctico también lo comprueba correctamente. Copia esta edición y pégala a continuación. Cambia Daisy por Mavericky pon su etiqueta en Status: Healthy.

60 lines | tests/Unit/Service/GithubServiceTest.php
// ... lines 1 - 9
use Symfony\Contracts\HttpClient\ResponseInterface;
class GithubServiceTest extends TestCase
{
// ... lines 14 - 16
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
// ... line 19
$mockHttpClient = $this->createMock(HttpClientInterface::class);
$mockResponse = $this->createMock(ResponseInterface::class);
$mockResponse
->method('toArray')
->willReturn([
[
'title' => 'Daisy',
'labels' => [['name' => 'Status: Sick']],
],
[
'title' => 'Maverick',
'labels' => [['name' => 'Status: Healthy']],
],
])
;
$mockHttpClient
->method('request')
->willReturn($mockResponse)
;
// ... lines 41 - 44
}
// ... lines 46 - 58
}

¡Perfecto! Nuestras afirmaciones ya esperan que Daisy esté enfermo y Mavericksano. Así que, si nuestras pruebas pasan, significa que toda nuestra lógica de análisis de etiquetas es correcta.

Crucemos los dedos y probemos:

./vendor/bin/phpunit

Y... ¡Genial! ¡Pasan! Y lo mejor de todo, ¡ya no estamos llamando a la API de GitHub cuando ejecutamos nuestras pruebas! Imagínate el pánico que causaríamos si tuviéramos que bloquear el parque porque nuestras pruebas fallan porque la api está desconectada... o simplemente porque alguien ha cambiado las etiquetas en GitHub, Ya... Yo tampoco quiero ese dolor de cabeza...

¿Stubs? ¿Mocks?

¿Recuerdas cuando hablábamos de los diferentes nombres de los mocks? Pues bien, tantomockResponse como mockHttpClient se llaman ahora oficialmente stubs... Es una forma elegante de decir objetos falsos en los que, opcionalmente, tomamos el control de los valores que devuelven. Eso es exactamente lo que estamos haciendo con el método willReturn(). De nuevo, la terminología no es demasiado importante, pero ahí la tienes. Esto son stubs. Y sí, cada vez que enseño esto, tengo que buscar estos términos para recordar qué significan exactamente.

A continuación, vamos a convertir nuestros stubs en auténticos objetos simulados, probando también los datos pasados al objeto simulado.