Uma cartilha sobre reflexão em PHP (e como isso funciona no teste de unidade)
Nas últimas semanas, tenho escrito sobre testes unitários para os membros do site (e algo que pretendo continuar fazendo no próximo post). É algo que eu acho; se você escrever código do lado do servidor, você deve estar fazendo.
Claro, é mais fácil para mim dizer do que fazê-lo, então, embora eu tente fazer um bom trabalho, sempre há espaço para melhorar. Eu digo isso mais como uma verificação pessoal do que qualquer outra coisa, então eu discordo.
Um dos conceitos que surgem frequentemente durante os testes é a ideia de testar os valores dos atributos privados. Por exemplo, digamos que você tenha um setter, mas não necessariamente um getter para esse valor específico.
É fácil dizer "Bem, então você precisa escrever um getter", mas nem sempre é esse o caso. E se você estiver armazenando algumas informações dentro da classe que não precisam ser expostas a classes de terceiros?
Como então devemos escrever testes contra esse tipo de dados quando queremos acessá-los, mas não temos a capacidade de fazê-lo e não queremos comprometer a integridade do nosso trabalho?
É aí que entra a reflexão.
Reflexão em PHP
Especificamente, para entender como inspecionar o valor de uma determinada variável de fora para dentro, você precisa saber como usar a reflexão.
Felizmente, o PHP nos fornece uma API poderosa para fazer isso, e provavelmente vale a pena entrar nos detalhes profundos disso em outro post. Mas, para este, vai ficar com o básico.
Uma definição de trabalho
Primeiro, uma olhada no que significa reflexão. O manual do PHP define como :
A classe ReflectionClass relata informações sobre uma classe.
Mas acho que vale a pena ter algo um pouco mais sólido. Vamos com algo assim pelo menos para este post:
Reflexão é como um programa se inspeciona e se modifica durante o tempo de execução.
Talvez não seja ótimo; talvez não.
Mas servirá ao propósito deste post.
Lendo um valor por meio de reflexão
Vamos supor que para este post, você tenha uma classe de namespaces em AcmePluginAPIClient e tenha uma propriedade chamada username. Vamos dar uma olhada em como pode ser uma implementação muito básica disso mais tarde.
Claro, seria muito mais desenvolvido em um plugin real.
Digamos, no entanto, que você deseja definir o valor do atributo e, em seguida, ler seu valor. A ressalva é que a propriedade está marcada como privada e não há como lê-la de fora.
É aqui que a reflexão vem a calhar. Ou seja, podemos usar uma parte do programa para olhar para si mesmo e relatar o que vê. (Reflexão, entendeu? É como quando queremos saber o que está acontecendo conosco e ninguém mais está por perto, então olhamos no espelho e vemos o que está lá.)
Para fazer isso, você precisa fazer cinco coisas:
- Instancie a classe que deseja testar,
- Defina o valor da variável,
- Pegue uma instância do ReflectionClass para a classe que queremos testar,
- Defina sua propriedade como acessível,
- Leia o valor.
Então, aqui está uma série de essências que fornecerão as etapas necessárias para fazer exatamente isso.
1 Instanciar a classe
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
// Other functions for class implementation...
}
2 Defina o valor
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
public function setUsername($username)
{
$this->username = $username;
}
// Other functions for class implementation...
}
3 Instanciar Objetos Refletidos
<?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. Defina a propriedade, marque-a como acessível
<?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 Leia o 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);
}
}
E essa é a cartilha básica sobre reflexão
Neste ponto, isso deve fornecer algumas informações básicas sobre o que é o Reflection, como usá-lo e por que ele é útil, especialmente no caso de testes de unidade.
Este é um daqueles conceitos que podem ficar mais complexos porque a API de reflexão do PHP é bastante poderosa (mas relativamente fácil de entender). No entanto, quando você combina com testes de unidade, há muitas coisas que podem ser feitas.
Meu plugue sem vergonha
Com isso dito, se você estiver interessado em aprender os prós e contras desse tipo de coisa, não hesite em conferir a área exclusiva para membros do site. Estou construindo, a cada semana, uma lista de pendências para nos ajudar a nos concentrar em adotar as melhores práticas como desenvolvedores do WordPress.
Teste de unidade, reflexão e muito mais é apenas a parte mais recente.
