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, чтобы я мог писать тесты, чтобы убедиться, что свойства, которые, я думаю, получают и устанавливают, действительно действительно получают и устанавливают?
Или есть ли еще какая-то другая вещь, которую я должен делать в тесте, чтобы поймать такие проблемы, не пытаясь получить доступ к частному состоянию тестируемого объекта?
Ответы
Ответ 1
Для тестирования свойств я сделаю те же аргументы, которые я делаю, а затем говорю о тестировании частных методов.
You usually don't want to do this
.
Это о тестировании наблюдаемого поведения.
Если вы переименуете все свои свойства или решите сохранить их в массиве, вам не нужно будет вообще адаптировать ваши тесты. Вы хотите, чтобы ваши тесты говорили вам, что все еще работает! Когда вам нужно изменить тесты, чтобы убедиться, что все еще работает, вы теряете все преимущества, так как вы также можете сделать ошибку, изменяя тесты.
Итак, в целом, вы теряете ценность своего тестового набора!
Просто тестирование комбинаций get/set было бы достаточно, но обычно не каждый сеттер должен иметь getter, а просто создавать их для тестирования - это не простой эфир.
Обычно вы устанавливаете некоторые вещи, а затем передаете метод DO
(поведение). Тестирование для этого (что класс делает то, что должно делать) является наилучшим вариантом для тестирования и должен сделать проверку свойств лишними.
Если вы действительно хотите сделать это, есть функциональность setAccessible
в API рефлексии PHP, но я не могу составить пример, когда я нахожу это желательным
Поиск неиспользуемых свойств для поиска ошибок/проблем, подобных этому:
PHP Mess Detector Как UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
Это приведет к двум предупреждениям, потому что переменные никогда не доступны
Ответ 2
Вы также можете использовать Assert::assertAttributeEquals('value', 'propertyName', $object)
.
См. https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
Ответ 3
Я просто хочу указать на одно. Позвольте забыть о частных месторождениях на мгновение и сосредоточиться на том, что заботит клиент вашего класса. Ваш класс предоставляет контракт, в данном случае - возможность изменять и получать имя (через getter и setter). Ожидаемая функциональность проста:
- когда я устанавливаю имя с
setName
до "Gerald"
, я ожидаю получить "Gerald"
, когда я вызываю getName
Это все. Клиент не будет (ну, не стоит!) Заботиться о внутренней реализации. Используете ли вы имя частного поля, hashset или называете веб-сервис через динамически сгенерированный код - не имеет значения для клиента. Ошибка, с которой вы сейчас сталкиваетесь, с точки зрения пользователя - не является ошибкой вообще.
Если PHPUnit позволяет вам проверять частные переменные - я не знаю. Но с точки зрения модульного тестирования вы не должны этого делать.
Изменить (в ответ на комментарий):
Я понимаю ваши опасения по поводу возможного воздействия внутреннего состояния, однако я не думаю, что модульное тестирование - это правильный инструмент для решения этого. Вы можете придумать множество возможных сценариев, как что-то может сделать что-то еще, что не было запланировано. Модульные тесты ни в коем случае не вылечиваются для всех и не должны использоваться как таковые.
Ответ 4
Я согласен с остальными, что в целом вы хотите избежать доступа к частным лицам в своих тестах, но для случаев, когда вам нужно, вы можете использовать отражение для чтения и записи свойства.