Подмена защищенных и приватных свойств для тестирования
Иногда в тестах нам нужно прочитать/заменить 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.