Cómo testear con métodos privados en PHPUNIT
Tags: phpunit, php
Published: 2021-08-20 17:52:23

---
title: Cómo testear con métodos privados en PHPUNIT
published: true
description: Cómo testear métodos privados en PHPUNIT
tags: phpunit,php
//cover_image: https://direct_url_to_image.jpg
---

A la hora de desarrollar nuestros tests, podremos tener problemas con el ámbito de privacidad de los métodos.
Según las buenas prácticas en el desarrollo de test, estos métodos deben ser públicos, sin embargo, en algunos casos, en la implementación de una **api** estos métodos disponen de métodos privados y clases finales.

Aquí el ejemplo:
```php

/**
     * @param array $data
     *
     * @return array
     */
    private function deserializeCompetitions(array $data): array
    {
        $sportArray = [];

        foreach ($data as $entry) {
            if (!empty($entry['id'])) {
                $sportArray[] = $this->deserializeCompetition($entry);
            }
        }

        return $sportArray;
    }
```
Si queremos deserializar los datos para poder validar alǵun método que estemos desarrollando la única manera 'sana', para poder realizar este proceso es utilizar el propio o los propios métodos de la clase que los implementa.
El problema es que estos métodos son privados, y la clase que lo implementa es una clase **final**. Perfectamente lógico y además [bien hecho](https://ocramius.github.io/blog/when-to-declare-classes-final).

En este caso, y para salir del paso, podemos usar la [Api de de reflexión](https://www.php.net/manual/es/intro.reflection.php) de PHP para manipular el ámbito de los métodos. 

Esto es:
``` php
    // obtenemos un nuevo objeto con la clase ReflectionMethod .
    // los párametros son la clase que implementa el método privado, y el nombre del método privado 
    $method = new ReflectionMethod(SportsDataClient::class, 'deserializeCompetition');
    // lo configuramos para que pueda ser accesible
    $method->setAccessible(true);
    // instanciamos nuestra clase a testar
    $testClient = new SportdataClientTest();
    $contentClient = $testClient->createInstance();
    // recuperamos los datos que queremos deserializar en formato por ejemplo .json
    $optaCompetition = $this->getOptaJson('opta-competition')['competition'];
    // con _invoke, ejecutamos el método ahora accesible junto con los datos de entrada
    // los parámetros son la clase que implementa el método y el/los parámetros de entrada
    $competition = $method->invoke($contentClient, $optaCompetition);
    // creamos un Mock, con lo que podremos retornar un valor de la clase Competition
    // con los datos deserializados en el formato correspondiente.
    $sportDataclientMock
        ->expects($this->any())
        ->method('getCompetitionBySportIdAndCompetitionId')
        ->willReturn($competition);
```

Y nuestro método que necesitamos :

```php    
/**
     * @param array $data
     *
     * @return Competition
     */
    private function deserializeCompetition(array $data): Competition
    {
        $sportsId = SportsDataId::build($data['id']);
        $seasons = $this->deserializeSeasons($data);

        return Competition::build(
            $sportsId,
            $data['name'] ?? '',
            $data['short_name'] ?? '',
            $data['url'] ?? '',
            $data['path'] ?? '',
            $data['active_season'] ?? '',
            $seasons
        );
    }
```
De esta forma, no saltará ninguna excepción de PHP UNIT por intentar acceder a un método privado desde una clase final.

fdo: **roberto manchado**