Una introducción a la reflexión en PHP (y cómo juega en las pruebas unitarias)
Durante las últimas semanas, he estado escribiendo sobre pruebas unitarias para los miembros del sitio (y algo que planeo seguir haciendo en la próxima publicación). Es algo que pienso; si escribe código del lado del servidor, debería hacerlo.
Por supuesto, es más fácil para mí decirlo que hacerlo, así que aunque trato de asegurarme de hacer un buen trabajo, siempre hay margen para mejorar. Digo eso más como un control personal que cualquier otra cosa, así que estoy divagando.
Uno de los conceptos que a menudo surgen durante las pruebas es la idea de probar los valores de los atributos privados. Por ejemplo, supongamos que tiene un setter, pero no necesariamente tiene un getter para ese valor en particular.
Es fácil decir "Bueno, entonces necesitas escribir un captador", pero no siempre es así. Quiero decir, ¿qué pasa si estás almacenando información dentro de la clase que no necesita estar expuesta a clases de terceros?
Entonces, ¿cómo se supone que debemos escribir pruebas contra ese tipo de datos cuando queremos acceder a ellos pero no tenemos la capacidad de hacerlo y no queremos comprometer la integridad de nuestro trabajo?
Ahí es donde entra en juego la reflexión.
Reflexión en PHP
Específicamente, para comprender cómo inspeccionar el valor de una variable dada desde afuera hacia adentro, debe saber cómo usar la reflexión.
Afortunadamente, PHP nos brinda una poderosa API para hacer eso, y probablemente valga la pena profundizar en los detalles de eso en otra publicación. Pero, para este, me quedo con lo básico.
Una definición de trabajo
Primero, un vistazo a lo que significa reflexión. El manual de PHP lo define como :
La clase ReflectionClass informa sobre una clase.
Pero creo que vale la pena tener algo un poco más sólido. Vamos con algo así al menos para este post:
La reflexión es cómo un programa se inspecciona a sí mismo y se modifica a sí mismo durante el tiempo de ejecución.
Tal vez no sea genial; tal vez no.
Pero servirá para el propósito de esta publicación.
Lectura de un valor a través de la reflexión
Supongamos que para esta publicación, tiene una clase de espacios de nombres en AcmePluginAPIClient y tiene una propiedad llamada nombre de usuario. Echaremos un vistazo a cómo puede verse una implementación muy básica de esto más adelante.
Por supuesto, estaría mucho más desarrollado en un complemento real.
Sin embargo, supongamos que desea establecer el valor del atributo y luego leer su valor. La advertencia es que la propiedad está marcada como privada y no hay forma de leerla desde el exterior.
Aquí es donde la reflexión es útil. Es decir, podemos usar una parte del programa para mirarse a sí mismo y reportar lo que ve. (Reflexión, ¿entiendes? Es como cuando queremos saber qué pasa con nosotros mismos y no hay nadie más alrededor, así que nos miramos en un espejo y vemos qué hay allí).
Para hacer esto, necesitas hacer cinco cosas:
- Cree una instancia de la clase que desea probar,
- Establecer el valor de la variable,
- Tome una instancia de ReflectionClass para la clase que queremos probar,
- Establezca su propiedad en accesible,
- Leer el valor.
Así que aquí hay una serie de puntos esenciales que le proporcionarán los pasos necesarios para hacer exactamente eso.
1 Instanciar la clase
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
// Other functions for class implementation...
}
2 Establecer el valor
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
public function setUsername($username)
{
$this->username = $username;
}
// Other functions for class implementation...
}
3 Crear instancias de objetos reflejados
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
// More to come...
}
}
4. Configure la propiedad, márquela como accesible
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
$usernameProperty = new ReflectionObject($client);
$usernameProperty->setAccessible(true);
// More to come...
}
}
5 Leer el valor
<?php
namespace AcmePluginTests;
use AcmePluginAPIAPIClient;
class APIClientTest
{
public function setUsername()
{
// Instantiate the class.
$client = new APIClient();
$username = 'tommcfarlin';
$client->setUsername($username);
// Now get a reflected instance of the class.
$reflectedClient = new ReflectionClass('AcmePluginAPIAPIClient');
// Grab a reference to the private property by making it accessible.
$usernameProperty = new ReflectionObject($client);
$usernameProperty->setAccessible(true);
// And finally, read it's value.
$usernameValue = $usernameProperty->getValue($client);
}
}
Y ese es el manual básico sobre la reflexión
En este punto, esto debería brindarle información básica sobre qué es Reflection, cómo usarlo y por qué es útil, especialmente en el caso de las pruebas unitarias.
Este es uno de esos conceptos que pueden volverse más complejos porque la API de reflexión de PHP es bastante poderosa (pero relativamente fácil de entender). Sin embargo, cuando lo combina con pruebas unitarias, hay muchas cosas que se pueden hacer.
mi enchufe desvergonzado
Dicho esto, si está interesado en conocer los entresijos de este tipo de cosas, no dude en consultar el área exclusiva para miembros del sitio. Estoy creando, cada semana, una acumulación de cosas para ayudarnos a enfocarnos en adoptar mejores prácticas como desarrolladores de WordPress.
Unit Testing, Reflection y más es solo la última parte.
