PHPUnit: выполнение утверждений для непубличных переменных - PullRequest
24 голосов
/ 19 января 2012

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

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

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

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);

Будет выведено:

string (6) "gerald"

string (6) "gerald"

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

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

Ответы [ 4 ]

38 голосов
/ 03 октября 2013

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

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

20 голосов
/ 19 января 2012

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

You usually don't want to do this.

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

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

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


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

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


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

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

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++;
    }
}

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

3 голосов
/ 19 января 2012

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

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

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

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

Редактировать (в ответ на комментарий):

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

2 голосов
/ 19 января 2012

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

...