Учебник по рефлексии в PHP (и как она влияет на модульное тестирование)
В течение последних нескольких недель я писал о модульном тестировании для участников сайта (и кое-что, что я планирую продолжить в следующем посте). Это то, что я думаю; если вы пишете серверный код, вы должны делать.
Конечно, мне легче сказать это, чем сделать, поэтому, хотя я стараюсь быть уверенным, что делаю это хорошо, мне всегда есть куда совершенствоваться. Я говорю это больше для личной проверки, чем для чего-либо еще, поэтому я отвлекся.
Одна из концепций, которая часто возникает во время тестирования, — это проверка значений частных атрибутов. Например, предположим, что у вас есть установщик, но не обязательно есть приемник для этого конкретного значения.
Легко сказать: «Ну, тогда вам нужно написать геттер», но это не всегда так. Я имею в виду, что если вы храните какую-то информацию внутри класса, которую не нужно предоставлять сторонним классам?
Как же тогда мы должны писать тесты для такого рода данных, когда мы хотим получить к ним доступ, но не имеем возможности сделать это и не хотим ставить под угрозу целостность нашей работы?
Вот где отражение вступает в игру.
Отражение в PHP
В частности, чтобы понять, как проверить значение данной переменной снаружи внутрь, вы должны знать, как использовать отражение.
К счастью, PHP предоставляет нам мощный API для этого, и, вероятно, стоит подробно остановиться на этом в другом посте. Но для этого я буду придерживаться основ.
Рабочее определение
Во-первых, посмотрим, что означает отражение. Руководство PHP определяет его как :
Класс ReflectionClass сообщает информацию о классе.
Но думаю стоит иметь что-то посолиднее. Давайте сделаем что-то вроде этого, по крайней мере, для этого поста:
Рефлексия — это то, как программа проверяет себя и модифицирует себя во время выполнения.
Может быть, это не здорово; возможно, нет.
Но это послужит цели этого поста.
Чтение значения через отражение
Предположим, что для этого поста у вас есть класс пространств имен в AcmePluginAPIClient, и у него есть свойство с именем username. Позже мы посмотрим, как может выглядеть очень простая реализация этого.
Конечно, в реальном плагине это было бы гораздо более конкретизировано.
Предположим, однако, что вы хотите установить значение атрибута, а затем прочитать его значение. Предостережение заключается в том, что свойство помечено как частное и нет возможности прочитать его извне.
Здесь на помощь приходит рефлексия. То есть мы можем использовать часть программы, чтобы посмотреть на себя и сообщить, что она видит. (Отражение, понимаете? Это похоже на то, когда мы хотим знать, что с нами, а вокруг никого нет, поэтому мы смотрим в зеркало и видим, что там.)
Для этого нужно сделать пять вещей:
- Создайте экземпляр класса, который вы хотите протестировать,
- Установите значение переменной,
- Возьмите экземпляр ReflectionClass для класса, который мы хотим протестировать,
- Установите для него доступное свойство,
- Прочтите значение.
Итак, вот ряд сущностей, которые предоставят шаги, необходимые для того, чтобы сделать именно это.
1 Создайте экземпляр класса
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
// Other functions for class implementation...
}
2 Установите значение
<?php
namespace AcmePlugin;
class APIClient
{
private $username;
public function setUsername($username)
{
$this->username = $username;
}
// Other functions for class implementation...
}
3 Создание экземпляров отраженных объектов
<?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. Установите свойство, отметьте его как доступное
<?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 Прочтите значение
<?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);
}
}
И это основной учебник по рефлексии
На данный момент это должно дать вам некоторую базовую информацию о том, что такое Reflection, как его использовать и почему он полезен, особенно в случае модульного тестирования.
Это одна из тех концепций, которые могут стать более сложными, потому что API рефлексии PHP довольно мощный (но относительно простой для понимания). Однако, когда вы сочетаете это с модульным тестированием, можно сделать много вещей.
Моя бесстыдная пробка
С учетом сказанного, если вы заинтересованы в изучении всех тонкостей такого рода вещей, не стесняйтесь проверить раздел сайта только для членов. Каждую неделю я создаю список невыполненных работ, чтобы помочь нам сосредоточиться на использовании лучших практик разработчиков WordPress.
Модульное тестирование, рефлексия и многое другое — это только последняя часть.
