I started learning the Zend Framework a couple of years ago after this tutorial . It shows that mappers are created using the Zend\Db\Adapter\Adapter
class to connect to the database, and this is how I worked with the databases, since without problems.
Now I am trying to learn how to use PHPUnit for Zend applications and am having difficulty testing functions in mapper, since I cannot mock the Zend\Db\Adapter\Adapter
class.
This tutorial on the Zend website shows mocking database connections, but uses the Zend\Db\TableGateway\TableGateway
. All other tutorials I found on the Internet also use this class, and the only thing I found regarding the Zend\Db\Adapter\Adapter
class is this :
$date = new DateTime(); $mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); $mockStatement->expects($this->once())->method('execute')->with($this->equalTo(array( 'timestamp' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) ))); $mockDbDriver = $this->getMockBuilder('Zend\Db\Adapter\Driver\Pdo\Pdo') ->disableOriginalConstructor() ->getMock(); $mockDbAdapter = $this->getMock('Zend\Db\Adapter\Adapter', array(), array($mockDbDriver)); $mockDbAdapter->expects($this->once()) ->method('query') ->will($this->returnValue($mockStatement));
I tried to put this in my setUp
method, but when phpunit
in the test class, the following error occurs:
Fatal error: calling createStatement () member function on null in C: \ Program Files (x86) \ Zend \ Apache2 \ htdocs \ test_project \ vendor \ zendframework \ zend-db \ src \ Sql \ Sql.php on line 128
So my question is: how do you mock the Zend\Db\Adapter\Adapter
class in PHPUnit?
I saw this question which is similar, but uses Zend/Db/Adapter/AdapterInterface
, and I cannot translate this code into my situation. Class code and class code below.
ProductMapper.php:
public function __construct(Adapter $dbAdapter) { $this->dbAdapter = $dbAdapter; $this->sql = new Sql($dbAdapter); } public function fetchAllProducts() { $select = $this->sql->select('products'); $statement = $this->sql->prepareStatementForSqlObject($select); $results = $statement->execute(); $hydrator = new ClassMethods(); $product = new ProductEntity(); $resultset = new HydratingResultSet($hydrator, $product); $resultset->initialize($results); $resultset->buffer(); return $resultset; }
ProductMapperTest.php:
public function setUp() { $date = new DateTime(); $mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); $mockStatement->expects($this->once())->method('execute')->with($this->equalTo(array( 'timestamp' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) ))); $mockDbDriver = $this->getMockBuilder('Zend\Db\Adapter\Driver\Pdo\Pdo')->disableOriginalConstructor()->getMock(); $this->mockDbAdapter = $this->getMock('Zend\Db\Adapter\Adapter', array(), array( $mockDbDriver )); $this->mockDbAdapter->expects($this->once())->method('query')->will($this->returnValue($mockStatement)); } public function testFetchAllProducts() { $resultsSet = new ResultSet(); $productMapper = new ProductMapper($this->mockDbAdapter); $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); }
EDIT. # one:
Following Wilt's answer, I changed my mapper to use the Sql
class in the constructor and changed my test class to:
public function setUp() { $mockSelect = $this->getMock('Zend\Db\Sql\Select'); $mockDbAdapter = $this->getMockBuilder('Zend\Db\Adapter\AdapterInterface')->disableOriginalConstructor()->getMock(); $this->mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); $this->mockSql = $this->getMock('Zend\Db\Sql\Sql', array('select', 'prepareStatementForSqlObject'), array($mockDbAdapter)); $this->mockSql->method('select')->will($this->returnValue($mockSelect)); $this->mockSql->method('prepareStatementForSqlObject')->will($this->returnValue($this->mockStatement)); } public function testFetchAllProducts() { $resultsSet = new ResultSet(); $this->mockStatement->expects($this->once())->method('execute')->with()->will($this->returnValue($resultsSet)); $productMapper = new ProductMapper($this->mockSql); $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); }
However, now I get the following error:
Producttest \ Model \ ProductMapperTest :: testFetchAllProducts Failed to claim that two variables refer to the same object.
What comes from the string $this->assertSame($resultsSet, $productMapper->fetchAllProducts());
. Am I making fun of something wrong?
Edit # 2:
As suggested by Wilt, I changed the test class to using StatementInterface
to shout out an instruction instead, so now the code looks like this:
public function setUp() { $mockSelect = $this->getMock('Zend\Db\Sql\Select'); $mockDbAdapter = $this->getMockBuilder('Zend\Db\Adapter\AdapterInterface')->disableOriginalConstructor()->getMock(); $this->mockStatement = $this->getMock('Zend\Db\Adapter\Driver\StatementInterface'); $this->mockSql = $this->getMock('Zend\Db\Sql\Sql', array('select', 'prepareStatementForSqlObject'), array($mockDbAdapter)); $this->mockSql->method('select')->will($this->returnValue($mockSelect)); $this->mockSql->method('prepareStatementForSqlObject')->will($this->returnValue($this->mockStatement)); } public function testFetchAllProducts() { $resultsSet = new ResultSet(); $this->mockStatement->expects($this->once())->method('execute')->with()->will($this->returnValue($resultsSet)); $productMapper = new ProductMapper($this->mockSql); $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); }
But the test case still does not work as indicated above. I did not change the line of code that mocks the execute
method, as I believe it already returned $resultsSet
, however I could be wrong!