Я пытаюсь протестировать класс, который управляет доступом к данным в базе данных (по сути, CRUD). Используемая нами библиотека БД имеет API, в котором вы сначала получаете объект таблицы статическим вызовом:
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
... вы поняли.
Мы пытаемся протестировать этот метод, но имитируем материал DataObject, так что (а) нам не нужно фактическое соединение с базой данных для теста и (б) нам даже не нужно включать библиотеку DB_DataObject для теста .
Однако в PHPUnit я не могу заставить $ this-> getMock () надлежащим образом настроить статический вызов. У меня есть...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
... но в тесте все равно написан неизвестный метод "фабрика". Я знаю, что он создает объект, потому что раньше он сказал, что не может найти DB_DataObject. Теперь это возможно. Но нет метода?
Что я действительно хочу сделать, так это получить два фиктивных объекта, один для возвращаемого объекта таблицы. Итак, мне нужно не только указать, что factory является статическим вызовом, но также и то, что он возвращает некоторый указанный другой фиктивный объект, который я уже настроил.
Я должен упомянуть в качестве предостережения, что я сделал это в SimpleTest некоторое время назад (не могу найти код), и он работал нормально.
Что дает?
[ОБНОВИТЬ]
Я начинаю понимать, что это как-то связано с ожидаемыми ()






Это хороший пример зависимости в вашем коде - дизайн сделал невозможным внедрение в Mock, а не в реальном классе.
Моим первым предложением было бы попытаться реорганизовать код, чтобы использовать экземпляр, а не статический вызов.
Чего не хватает (или нет?) В вашем классе DB_DataObject, так это установщика для передачи подготовленного объекта db перед вызовом фабричного метода. Таким образом, вы можете передать макет или пользовательский объект db (с тем же интерфейсом), если возникнет необходимость.
В вашей тестовой настройке:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
Метод factory () должен возвращать фиктивный экземпляр БД. Если он еще не интегрирован в ваш класс, вам, вероятно, придется реорганизовать метод factory (), чтобы он заработал.
Я согласен с вами обоими, что лучше не использовать статический вызов. Однако, думаю, я забыл упомянуть, что DB_DataObject - это сторонняя библиотека, а статический вызов - это лучшая практика их для использования их кода, а не нашего. Есть и другие способы использования их объектов, которые включают непосредственное построение возвращаемого объекта. Он просто оставляет эти проклятые инструкции include / require в любом файле класса, который использует этот класс DB_DO. Это отстой, потому что тесты сломаются (или просто не будут изолированы), если вы тем временем пытаетесь имитировать одноименный класс в своем тесте - по крайней мере, я думаю.
Требуется ли / включить файл класса для DB_DataObject в свой тестовый пример? Если класс не существует до того, как PHPUnit попытается имитировать объект, вы можете получить подобные ошибки.
Если вы не можете изменить библиотеку, измените доступ к ней. Реорганизуйте все вызовы DB_DataObject :: factory () в метод экземпляра в вашем коде:
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
Теперь вы можете использовать частичную имитацию класса, который вы тестируете, и заставить getTable () вернуть фиктивный объект таблицы.
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
С помощью расширения PHPUnit MockFunction плюс runkit вы также можете имитировать статические методы. Будьте осторожны, потому что это исправление обезьяны, и поэтому его следует использовать только в крайних случаях. Не заменяет хорошие методы программирования.