Reputation: 328
I am adding unit tests to an existing project that is using namespaces. I haven't ever had to use namespaces before, so it is somewhat of an adventure. My issue is that in my unit tests, it appears the mocked methods are still being called. Below is an example of the code file and the test.
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;
}
Here is the test code:
// 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));
}
At this point, I'm just trying to get the test to execute to the end, but I continue to get the following error:
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
This tells me that the 'getAdapter', 'query' and 'execute' calls are still being made even though all of them are theoretically mocked. I've verified as best I can that the class names used are using the correct namespaces. Any ideas?
Upvotes: 3
Views: 391
Reputation: 24576
The problem is not in the namespaces. It's probably that you try to mock the class under test itself and I'm guessing that getAdapter()
is a private method, called from within the class.
Remember that a mock is an object of a generated class that extends the original class.
Now, there's Mock_XYZ extends BalloonModel
which adds the mock method getAdapter()
, but if the getAdapter()
method in BalloonModel
itself is private, it will not be overridden but you end up with two different methods (one internal and one external if you will).
Refactor your code to use Dependency Injection. I'm not talking about IoC containers, just about creating other objects not in the class itself, but inject them with setters or constructor. Then you can do the following after creating the $adapter
mock, instead of mocking getAdapter()
:
$bm->setAdapter($adapter);
Upvotes: 1