Лучшие практики для тестирования защищенных методов с помощью PHPUnit (на абстрактных классах) - PullRequest
12 голосов
/ 16 февраля 2011

С 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: Вопрос НЕ в истинности тестирования защищенных методов (см .: тестирование белого, серого и черного ящика). Необходимость тестирования защищенных методов зависит от вашей стратегии тестирования.

Ответы [ 2 ]

24 голосов
/ 16 февраля 2011

Поскольку вы запрашиваете «лучшую практику», я отвечу по-другому:

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

То, что вы можете, не означает, что вы должны.

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

Тебе все равно, как это реализовано в классе.

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

  • Время

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

  • Значимое покрытие кода

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

Цитировать PHPUnit Автор

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

http://sebastian -bergmann.de / архив / 881-Testing-Your-Privates.html

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

...->setAccessible() хорошо для нормальных методов

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

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

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

5 голосов
/ 16 февраля 2011

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

Создать фиктивный объект для абстрактного класса и передать его этому измененномуform 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(), чтобы решить, как построить объект.

...