几年前我在this tutorial之后开始学习Zend Framework。在那里,它显示使用Zend\Db\Adapter\Adapter
类创建映射器以获取数据库连接,这就是我使用数据库的方式,因为没有问题。
我现在正在尝试学习如何在Zend应用程序上使用PHPUnit,并且在测试mapper中的函数时遇到了困难,因为我无法模拟Zend\Db\Adapter\Adapter
类。
This tutorial显示了模拟数据库连接,但它使用了Zend\Db\TableGateway\TableGateway
类。我在网上找到的所有其他教程也使用这个课程,我发现Zend\Db\Adapter\Adapter
课程的唯一内容是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));
我已尝试将其放入我的setUp
方法,但在测试类上运行phpunit
会出现以下错误:
致命错误:在C:\ Program Files(x86)\ Zend \ Apache2 \ htdocs \ test_project \ vendor \ zendframework \ zend-db \ src \ Sql \ Sql.php中调用null上的成员函数createStatement()第128行
所以我的问题是,你如何在PHPUnit中模拟Zend\Db\Adapter\Adapter
类?
我看到this question类似,但使用的是Zend/Db/Adapter/AdapterInterface
,而我似乎无法将该代码转换为我的情况。 Mapper和测试类代码如下。
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());
}
编辑#1:
继Wilt的回答之后,我改变了我的mapper以使用构造函数中的Sql
类,并将我的Test类更改为:
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());
}
但是,我现在收到以下错误:
ProductTest \模型\ ProductMapperTest :: testFetchAllProducts 断言两个变量引用同一个对象失败。
哪一行来自$this->assertSame($resultsSet, $productMapper->fetchAllProducts());
行。我是不是错误地嘲笑了什么?
编辑#2:
正如Wilt所建议的,我改变了测试类,使用StatementInterface
代替模拟语句,所以代码现在看起来像:
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());
}
但是测试用例仍然如上所述失败。我没有更改模拟execute
方法的代码行,因为我相信它已经返回$resultsSet
,但我可能错了!
答案 0 :(得分:3)
也许在这里更好地更改__construct
方法以将Sql
实例作为参数。似乎$dbAdapter
仅在构造函数中使用,因此在我看来,ProductMapper
类的实际依赖关系不是Adapter
实例,而是Sql
实例1}}实例。如果您进行了更改,则只需模拟Sql
内的ProductMapperTest
课程。
如果您不想在代码中进行此类更改,并且仍希望继续为当前ProductMapper
类编写测试,则还应该模拟{{1}的所有其他方法Adapter
类在内部调用的类。
现在,您在Sql
实例上调用$this->sql->prepareStatementForSqlObject($select);
,内部调用Sql
类的createStatement
方法(您可以看到here on line 128 inside the Sql
class)。但在你的情况下,Adapter
是一个模拟,这就是抛出错误的原因:
致命错误:在 128
上的Adapter
中调用null成员函数createStatement()
所以要解决这个问题,你应该模仿这个方法,就像你对C:\Program Files (x86)\Zend\Apache2\htdocs\test_project\vendor\zendframework\zend-db\src\Sql\Sql.php
方法所做的那样:
query
在下一行中,您拨打$mockStatement = //...your mocked statement...
$this->mockDbAdapter->expects($this->once())
->method('createStatement')
->will($this->returnValue($mockStatement));
,这意味着您还需要在$statement->execute();
内模仿execute
方法。
正如你所看到的那样,这个测试变得非常麻烦。你应该问问自己,你是否走在正确的道路上并测试正确的组件。您可以进行一些小的设计更改(改进),以便更轻松地测试$mockStatement
类。