Перегрузка PHP в частные свойства и методы модульного теста

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

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

function test__Construction_Should_Properly_Set_Tables() {
  $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
  $tables = $cv->tables;
  $this->assertEqual( $tables, $this->standardTableDef );
}

Затем я написал метод __get в CVObject следующим образом:

function __get( $name ) {
  $trace = debug_backtrace();
  $caller = $trace[1];
  $inTesting = preg_match( '/simpletest/', $caller['file'] );

  if ( $inTesting ) {
    return $this->$name;
  } else {
    trigger_error( 'Cannot access protected property CVObject::$' .
                     $name . ' in ' . $trace[0]['file'] . ' on line ' .
                     $trace[0]['line'],
                    E_USER_NOTICE );
  }
}

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

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


person Chuck    schedule 19.02.2011    source источник
comment
Кстати, SimpleTest больше не разрабатывается. PHPUnit – это стандартная среда тестирования xUnit, которая включает покрытие кода и фиктивные объекты для загрузки. Кроме того, debug_backtrace() — недешевый вызов, и его следует избегать в производственном коде.   -  person David Harkness    schedule 20.02.2011


Ответы (3)


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

Вы можете использовать метод подкласса Кена для защищенных свойств, но если вам нужен доступ к частному и вы используете PHP 5.3.2+, вы можете использовать отражение.

function test__Construction_Should_Properly_Set_Tables() {
    $cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
    $tables = self::getPrivate($cv, 'tables');
    $this->assertEqual( $tables, $this->standardTableDef );
}

static function getPrivate($object, $property) {
    $reflector = new ReflectionProperty(get_class($object), $property);
    $reflector->setAccessible(true);
    return $reflector->getValue($object);
}

Обратите внимание, что getPrivate() не будет работать, как написано, для свойств, унаследованных от суперклассов, но не так уж сложно зациклиться на иерархии, чтобы найти объявляющий класс.

person David Harkness    schedule 20.02.2011

При тестировании компонента вы должны тестировать только его интерфейс (ввод, вывод, исключения), не учитывая и даже не зная его внутренней реализации (еще лучше, если один программист пишет тестовые случаи, а другой реализует, см. XP и TDD техники). Итак, единственное, что вам нужно протестировать, — это общедоступные методы.

Чтобы убедиться, что ваши частные (вспомогательные) методы написаны правильно, просто используйте анализатор покрытия кода (пожалуйста, проверьте Инструменты покрытия кода для PHP) и покройте как можно больше кода своими тестовыми примерами.

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

person ljank    schedule 19.02.2011

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

class Foo
{
    protected function bar(){} // should really be private but protected will do
}

class FooTestWrapper extends Foo
{
    public function bar{} { return parent::bar(); } // this is testable
}

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

person Ken    schedule 19.02.2011