PHPUnit: Выполнение утверждений о непубличных переменных

Предположим, у меня есть класс с частным свойством и связанным с ним публичным getter и setter. Я хочу проверить с помощью PHPUnit, что свойство получает правильное значение после использования setter или что getter возвращает правильное свойство.

Конечно, я могу проверить сеттер, используя геттер, чтобы увидеть, что объект хранит правильное значение, и наоборот для тестирования геттера. Однако это не гарантирует, что частная собственность является той, которая установлена.

Скажем, у меня был следующий класс. Я создал свойство, getter и setter. Но я сделал опечатку в названии свойства, поэтому геттер и сеттер фактически не манипулируют имуществом, которое они предназначены для манипулирования

class SomeClass { private $mane = NULL; // Was supposed to be $name but got fat-fingered! public function getName () { return ($this -> name); } public function setName ($newName) { $this -> name = $newName; return ($this); } } 

Если я запустил следующий тест

 public function testSetName () { $this -> object -> setName ('Gerald'); $this -> assertTrue ($this -> object -> getName () == 'Gerald'); } 

Я бы получил пропуск. Однако на самом деле случилось что-то очень плохое, чего я не хочу. Когда вызывается setName (), он фактически создает новое свойство в классе с именем, которое, по моему мнению, имело мое личное свойство, только тот, который создает сеттер, является общедоступным! Я могу показать, что со следующим кодом:

 $a = new SomeClass; $a -> setName('gerald'); var_dump ($a -> getName ()); var_dump ($a -> name); 

Он будет выводить:

строка (6) "gerald"

строка (6) "gerald"

Можно ли каким-либо образом получить доступ к приватным свойствам из PHPUnit, чтобы я мог писать тесты, чтобы убедиться, что свойства, которые, как мне кажется, получают и устанавливаются на самом деле, действительно получают и устанавливают?

Или есть еще одна вещь, которую я должен делать в тесте, чтобы поймать такие проблемы, не пытаясь получить доступ к частному состоянию тестируемого объекта?

Для тестирования свойств я бы сделал те же аргументы, которые я делаю, а затем говорю о тестировании частных методов.

You usually don't want to do this .

Речь идет о тестировании наблюдаемого поведения.

Если вы переименуете все свои свойства или решите сохранить их в массиве, вам не нужно будет полностью адаптировать ваши тесты. Вы хотите, чтобы ваши тесты говорили вам, что все еще работает ! Когда вам нужно изменить тесты, чтобы убедиться, что все еще работает, вы теряете все преимущества, так как вы также можете сделать ошибку, изменяя тесты.

Итак, в целом, вы теряете ценность своего тестового набора!


Просто тестирование комбинаций get / set было бы достаточно, но обычно не каждый сеттер должен иметь getter, а просто создавать их для тестирования – это не простой эфир.

Как правило, вы устанавливаете некоторые вещи, а затем говорите методу DO (поведение) что-то. Тестирование для этого (что класс делает то, что должно делать) является наилучшим вариантом для тестирования и должен сделать проверку свойств излишней.


Если вы действительно хотите сделать это, есть функция setAccessible в API рефлексии PHP, но я не могу составить пример, где я считаю это желательным

Поиск неиспользуемых свойств для поиска ошибок / проблем, подобных этому:

Детектор PHP Mess как UnusedPrivateField Rule

 class Something { private static $FOO = 2; // Unused private $i = 5; // Unused private $j = 6; public function addOne() { return $this->j++; } } 

Это приведет к двум предупреждениям, потому что переменные никогда не доступны

Вы также можете использовать Assert::assertAttributeEquals('value', 'propertyName', $object) .

См. https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490

Я согласен с другими, что в целом вы хотите избежать доступа к частным лицам в своих тестах, но для случаев, когда вам нужно, вы можете использовать отражение, чтобы читать и писать свойство .

Я просто хочу отметить одно. Давайте забудем о частных полях на мгновение и сосредоточимся на том, что заботит клиент вашего класса. Ваш класс предоставляет контракт, в данном случае – возможность изменять и получать имя (через getter и setter). Ожидаемая функциональность проста:

  • когда я устанавливаю имя с setName в "Gerald" , я ожидаю получить "Gerald" когда я вызываю getName

Это все. Клиент не будет (ну, не стоит!) Заботиться о внутренней реализации. Используете ли вы имя частного поля, hashset или называете веб-сервис через динамически сгенерированный код – не имеет значения для клиента. Ошибка, с которой вы в настоящее время сталкиваетесь, с точки зрения пользователя – не является ошибкой.

Является ли PHPUnit проверять частные переменные – я не знаю. Но с точки зрения модульного тестирования вы не должны этого делать.

Изменить (в ответ на комментарий):

Я понимаю ваши опасения по поводу возможного воздействия внутреннего состояния, однако я не думаю, что модульное тестирование – это правильный инструмент для решения этого. Вы можете придумать множество возможных сценариев, как что-то может сделать что-то еще, что не было запланировано. Единичные тесты ни в коем случае не вылечиваются для всех и не должны использоваться как таковые.