Я добавляю модульные тесты в существующий проект, использующий пространства имен. Раньше мне никогда не приходилось использовать пространства имен, так что это своего рода приключение. Моя проблема в том, что в моих модульных тестах оказывается, что проверенные методы все еще вызываются. Ниже приведен пример файла кода и теста.
private function selectFromDb($fields, $criteria = null) {
$fields = is_array($fields) ? implode(', ', $fields) : $fields;
$sql = "SELECT $fields FROM balloons";
if(!is_null($criteria)) {
$sql .= " WHERE $criteria";
}
$adapter = $this->getAdapter();
$statement = $adapter->query($sql);
$result = $statement->execute();
return $result;
}
Вот тестовый код:
// I'm passing in data here which isn't consequential for the question.
public function testSelectFromDb($fields, $criteria, $expectedSql) {
$statement = $this->getMockBuilder('Zend\Db\Adapter\Driver\Pdo\Statement')
->disableOriginalConstructor()
->setMethods(array('execute'))->getMock();
$statement->expects($this->once())
->method('execute')->will($this->returnValue('fake'));
$adapter = $this->getMockBuilder('Zend\Db\Adapter\Adapter')
->disableOriginalConstructor()
->setMethods(array('query'))->getMock();
$adapter->expects($this->once())
->method('query')->with($expectedSql)
->will($this->returnValue($statement));
$bm = $this->getMockBuilder('Application\Model\BalloonModel')
->setMethods(array('getAdapter'))
->disableOriginalConstructor()->getMock();
$bm->expects($this->once())
->method('getAdapter')->will($this->returnValue($adapter));
// I use reflection as the method is private to the class
$reflection = new ReflectionClass($bm);
$method = $reflection->getMethod('selectFromDb');
$method->setAccessible(true);
$result = $method->invokeArgs($bm, array($fields, $criteria));
}
На данный момент я просто пытаюсь завершить тест до конца, но продолжаю получать следующую ошибку:
Tests\Model\BalloonModelTest::testSelectFromDb with data set "singleField" ('id', NULL, 'SELECT id FROM balloon')
Zend\Db\Adapter\Exception\InvalidQueryException: Statement could not be executed
/apath/PHP/vendor/zendframework/zendframework/library/zend/db/adapter/driver/pdo/statement.php:245
/apath/PHP/vendor/zendframework/zendframework/library/zend/db/adapter/driver/pdo/statement.php:240
/apath/PHP/module/Application/src/application/model/balloonmodel.php:243
/apath/PHP/tests/Model/BalloonModelTest.php:70
Caused by
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'id' in 'field list'
/apath/PHP/vendor/zendframework/zendframework/library/zend/db/adapter/driver/pdo/statement.php:240
/apath/PHP/module/Application/src/application/model/balloonmodel.php:243
/apath/PHP/tests/Model/BalloonModelTest.php:70
Это говорит мне о том, что вызовы ‘getAdapter’, ‘query’ и ‘execute’ все еще выполняются, хотя все они теоретически поддельные. Я как мог убедился, что используемые имена классов используют правильные пространства имен. Есть идеи?
Проблема не в пространствах имен. Вероятно, вы пытаетесь высмеять сам тестируемый класс, и я предполагаю, что getAdapter()
это частный метод, вызываемый из класса.
Помните, что макет — это объект сгенерированного класса, который расширяет исходный класс.
Теперь есть Mock_XYZ extends BalloonModel
который добавляет метод макета getAdapter()
, но если getAdapter()
метод в BalloonModel
сам по себе является частным, он не будет переопределен, но вы получите два разных метода (один внутренний и один внешний, если хотите).
Рефакторинг вашего кода для использования Dependency Injection. Я не говорю о контейнерах IoC, просто о создании других объектов не в самом классе, а о внедрении их с помощью сеттеров или конструктора. Затем вы можете сделать следующее после создания $adapter
издеваться, а не издеваться getAdapter()
:
$bm->setAdapter($adapter);
Других решений пока нет …