Подтвердить что ты не робот

Рекомендации по проверке защищенных методов с помощью PHPUnit (на абстрактных классах)

С PHPUnit и PHP >= 5.3 можно проверить защищенные методы. Следующая страница в stackoverflow описывает наилучшую практику:

"Рекомендации по проверке защищенных методов с помощью PHPUnit"

protected static function callProtectedMethod($name, $classname, $params) {
  $class = new ReflectionClass($classname);
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  $obj = new $classname($params);
  return $method->invokeArgs($obj, $params);
}

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

Я знаю, что PHPUnit выводит абстрактные классы и реализует абстрактные методы в конкретном классе и запускает тесты против этого конкретного класса, но я не знаю, как интегрировать это в описанный выше подход, чтобы иметь callProtectedMethodOnAbstractClasses().

Как вы проводите такие тесты?

PS: Вопрос не в истинности тестирования защищенных методов (см.: white-, gray- and blackbox-testing). Необходимость тестирования защищенных методов зависит от вашей стратегии тестирования.

4b9b3361

Ответ 1

Поскольку вы просите о "лучшей практике", я отвечу на другой подход:

Не проверяйте защищенные и частные методы

Просто потому, что вы можете это не значит, что вам нужно.

Вы хотите проверить, работает ли класс. Это означает, что все функции, которые вы можете вызывать, (все общедоступные) возвращать правильные значения (и, возможно, вызывать правильные функции на переданных объектах) и ничего еще.

Вам все равно, как это реализовано в классе.

Имхо, это даже мешает вам написать тест на что-нибудь непубличное по двум причинам:

  • Время

Написание тестов занимает больше времени, так как вам нужно больше, и рефакторинг также занимает больше времени. Если вы перемещаете код в классе без изменения его поведения, вам не потребуется обновлять его тесты. Тесты должны сказать вам, что все еще работает!

  • Значимый охват кода

Если вы пишете тест для каждого защищенного метода, вы потеряете одно наследуемое преимущество от отчета о покрытии кода: Он не скажет вам, какие защищенные функции больше не вызывают. То есть (imho) плохо, потому что вы либо не проверяете все общедоступные методы правильно (почему существует метод, который не вызывается, если вы проверяете каждый случай?), Либо вы действительно не нуждаетесь в этом методе больше, но поскольку он "зеленый", вы не задумываетесь об этом.

Чтобы процитировать автора PHPUnit

Итак: только потому, что тестирование защищенных и закрытых атрибутов и методов возможно, не означает, что это "хорошая вещь".

http://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html

Так как реальный мир иногда отличается

...->setAccessible() отлично подходит для обычных методов

для абстрактного использования материалов ...->getMockForAbstractClass()

Но, пожалуйста, сделайте это, только если это действительно необходимо.

Защищенный метод в абстрактном классе будет проверен путем тестирования публичного api его дочерних элементов в любом случае с использованием моих аргументов сверху.

Ответ 2

Предположение: Вы хотите вызвать конкретные защищенные методы в абстрактном классе.

Создайте макет объекта для абстрактного класса и передайте его в эту измененную форму callProtectedMethod().

public static function callProtectedMethod($object, $method, array $args=array()) {
    $class = new ReflectionClass(get_class($object));
    $method = $class->getMethod($method);
    $method->setAccessible(true);
    return $method->invokeArgs($object, $args);
}

public function testGetArea() {
    $rect = $this->getMockForAbstractClass('RandomRectangle');
    self::callProtectedMethod($rect, 'setWidth', array(7));
    self::callProtectedMethod($rect, 'setHeight', array(3));
    self::assertEquals(21, $rect->getArea());
}

Вы можете инкапсулировать это в один метод, но я предпочитаю передавать объект, чтобы тест мог вызывать несколько защищенных/приватных методов на одном и том же объекте. Для этого используйте $class->isAbstract(), чтобы решить, как построить объект.