Zend Framework 2 & PHPUnit - mock Zend \ Db \ Adapter \ Adapter class

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!

+6
source share
1 answer

Perhaps it would be better here to change the __construct method to take an Sql instance as an argument. It seems that $dbAdapter used only inside the constructor, and because of this, it seems to me that the actual dependency for your ProductMapper class is not an ProductMapper instance, but rather an Sql instance. If you make this change, you only need to make fun of the Sql class inside your ProductMapperTest .

If you do not want to make such a change inside your code, and you still want to continue the test for the current ProductMapper class, you should also mock all the other methods of the Adapter class that Sql calls internally.

Now you call $this->sql->prepareStatementForSqlObject($select); to your Sql instance, which internally calls the createStatement method of the Adapter class (you can see what 's on line 128 inside the Sql class ). But in your case, the Adapter is a layout, and therefore an error occurs:

Fatal error: calling createStatement() member createStatement() on zero in C:\Program Files (x86)\Zend\Apache2\htdocs\test_project\vendor\zendframework\zend-db\src\Sql\Sql.php on line 128

So, to solve this problem, you should also laugh at this method in the same way as you did for the query method:

 $mockStatement = //...your mocked statement... $this->mockDbAdapter->expects($this->once()) ->method('createStatement') ->will($this->returnValue($mockStatement)); 

In the next line, you call $statement->execute(); , which means that you will need to make fun of the execute method inside your $mockStatement .

As you can see, this test becomes rather cumbersome. And you should ask yourself if you are on the right track and checking the right components. You can make minor design changes (improvements) to make it easier to validate your ProductMapper class.

+3
source

Source: https://habr.com/ru/post/1014832/


All Articles