Вероятно, это довольно легко для некоторых из вас. Я пытаюсь проверить защищенный метод на небольшом классе соединений DB, который у меня есть.
Соответствующий код выглядит следующим образом:
class DbConnect{ /** * Connexion MSSQL local */ protected function localConnect($localconfig){ $connectionInfo = array("UID" => $localconfig->uid, "PWD" =>$localconfig->pwd, "Database"=> $localconfig->DB); $this->localConnection = sqlsrv_connect($localconfig->serverName, $connectionInfo); if( $this->localConnection === false ){ $sql_error = sqlsrv_errors(); throw new DBException("Error in DB Connection.\r\n SQL ERROR:" . $sql_error); } } }
Чтобы протестировать метод, у меня была яркая идея (вероятно, из сообщения здесь где-то) в подкласс и вызов оттуда. Я создал подкласс, прямо внизу моего тестового файла. Я, очевидно, не мог переопределить видимость метода для публики, поэтому решил еще один подход в заглушке: объявите открытый метод, который вызывает защищенный метод localConnect родителя:
class DBConnectStub extends DBconnect{ public function callLocalConnect($localConfig){ parent::localConnect($localConfig); } }
Мой тест теперь выглядит следующим образом:
/** * @expectedException DBException */ public function test_localConnectError(){ $localconfig = (object) array ( 'serverName' => 'nohost', 'uid' => 'nouid', 'pwd' => 'noPwd', 'DB' => 'noDB' ); $db = DbConnectStub::getInstance($localconfig, array()); $db->callLocalConnect($localConfig); unset($db);
}
Странная часть, когда я запускаю тест, php выплевывает:
Неустранимая ошибка: вызов неопределенного метода DbConnect :: callLocalConnect () в C: \ tirelinkCRMsync \ test \ tirelinkCRMSync \ DBConnectTest.php в строке 82.
Объект правильно инстанцирован, но почему метод не определен, конечно, есть деталь, которая ускользала от меня. Является ли этот подход действительным или есть лучший способ?
Это может быть глупый вопрос, но вы переопределили DbConnectStub :: getInstance, чтобы вернуть экземпляр Stub?
class DBConnectStub extends DBconnect{ public static function getInstance () { //whatever process to create the instance (and not the parent method call that will return a DBConnect instance) } public function callLocalConnect($localConfig){ parent::localConnect($localConfig); } }
Я пытаюсь проверить защищенный метод […]
Это так просто. Только не надо. Защищенные методы не являются частью общедоступного API классов, поэтому вы не должны делать предположений о том, как они работают, пытаясь убедиться, что ваш класс работает.
Вы должны иметь возможность изменить свой код (реализацию ваших публичных функций), не адаптируя свои тесты. То, для чего сделаны ваши тесты, чтобы вы могли изменить свой код, и вы уверены, что он все еще работает. Вы не можете быть уверены, что ваш код по-прежнему работает, как раньше, когда вы одновременно меняете свой код и свои тесты!
См .: Sebastian Bergmann -Testing Your Privates.html
Итак: просто потому, что тестирование защищенных и частных атрибутов и методов возможно, не означает, что это «хорошая вещь».
и: Best practices to test protected methods with PHPUnit - on abstract classes
То, что этот пост также упоминает, – это просто использовать
$method = new ReflectionMethod( 'Foo', 'doSomethingPrivate' ); $method->setAccessible(TRUE);
Это проще, чем создать подкласс для каждого метода, который вы хотите протестировать.
Pedantic side node:
Имхо должен быть $this->localConnect
а не parent::localConnect
потому что parent :: предназначен только для вызова того же метода родительского класса. (Не важно, просто смущает, по крайней мере, для меня).