Подмена защищенных и приватных свойств для тестирования
Иногда в тестах нам нужно прочитать/заменить protected
или private
свойство объекта.
Когда данный трюк может пригодится?
Если вам понадобился данный трюк то скорее всего вы имеете проблему в архитектуре вашего класса/приложения. Его использование это крайняя мера. Лучше всего тестировать приватные свойства и методы через публичные методы. Использование данного трюка оправдано в случае экономии времени и только, как временное решение.
Пример тестируемого класса:
class Suit {
private $private_property = 'private property value';
}
Чтение защищенных и приватных свойств
Чтобы прочитать свойство $private_property на другое нам поможет ReflectionProperty
. Создаем метод read_inaccessible_property
:
private function read_inaccessible_property( $object, string $property_name ) {
$property = new ReflectionProperty( $object, $property_name );
$property->setAccessible( true );
$value = $property->getValue( $object );
$property->setAccessible( false );
return $value;
}
Разберем работу метода построчно:
- С помощью
ReflectionProperty
получаем в виде объекта свойствоprivate_property
объекта классаSuit
; - Делаем свойство доступным для редактирования;
- Читаем свойство
private_property
объекта классаSuit
; - Делаем свойство недоступным для редактирования;
- И возвращаем результат.
Пример теста полностью:
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
class Test_Suit extends \PHPUnit\Framework\TestCase {
private function read_inaccessible_property( $object, string $property_name ) {
$property = new ReflectionProperty( $object, $property_name );
$property->setAccessible( true );
$value = $property->getValue( $object );
$property->setAccessible( false );
return $value;
}
public function test_read_property() {
$suit = new Suit();
$property = $this->read_inaccessible_property( $suit, 'private_property' );
$this->assertSame( 'private property value', $property );
}
}
Обновление защищенных и приватных свойств
Для замены защищенного/приватного свойства нам понадобится все тот же ReflectionProperty
. Создадим метод update_inaccessible_property
:
private function update_inaccessible_property( $object, string $property_name, $value ) {
$property = new ReflectionProperty( $object, $property_name );
$property->setAccessible( true );
$property->setValue( $object, $value );
$property->setAccessible( false );
}
Разберем построчно:
- С помощью
ReflectionProperty
получаем в виде объекта свойствоprivate_property
объекта классаSuit
; - Делаем свойство доступным для редактирования;
- Устанавливаем для свойства
private_property
объекта классаSuit
новое значение; - Делаем свойство недоступным для редактирования;
Пример теста полностью:
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
class Test_Suit extends \PHPUnit\Framework\TestCase {
private function read_inaccessible_property( $object, string $property_name ) {
$property = new ReflectionProperty( $object, $property_name );
$property->setAccessible( true );
$value = $property->getValue( $object );
$property->setAccessible( false );
return $value;
}
private function update_inaccessible_property( $object, string $property_name, $value ) {
$property = new ReflectionProperty( $object, $property_name );
$property->setAccessible( true );
$property->setValue( $object, $value );
$property->setAccessible( false );
}
public function test_update_private_property() {
$suit = new Suit();
$this->update_inaccessible_property( $suit, 'private_property', 'new private property value' );
$property = $this->read_inaccessible_property( $suit, 'private_property' );
$this->assertSame( 'new private property value', $property );
}
}
Источник: WP Punk.