几年前this tutorial之后,我开始学习Zend Framework。在那里,它显示了使用Zend\Db\Adapter\Adapter类创建映射器以获取数据库连接,这就是我一直以来使用数据库的方式,因为没有问题。

我现在正在尝试学习如何在Zend应用程序上使用PHPUnit,并且由于无法模拟Zend\Db\Adapter\Adapter类而在测试映射器中的功能时遇到了困难。

Zend网站上的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会出现以下错误:



所以我的问题是,如何在PHPUnit中模拟Zend\Db\Adapter\Adapter类?

我看过this question,它很相似,但是使用了Zend/Db/Adapter/AdapterInterface,我似乎无法将该代码转换为我的情况。映射器和测试类代码如下。

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的回答,我将映射器更改为在构造函数中使用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());
}

但是,我现在收到以下错误:



这是从$this->assertSame($resultsSet, $productMapper->fetchAllProducts());行来的。我 mock 了什么吗?

编辑#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,但是我可能错了!

最佳答案

也许最好更改__construct方法,以Sql实例作为参数。似乎$dbAdapter仅在构造函数内部使用,因此在我看来,您的ProductMapper类的实际依赖项不是Adapter实例,而是Sql实例。如果进行了更改,则只需要在Sql中模拟ProductMapperTest类。

如果您不想在代码内进行此类更改,但仍然希望继续为当前ProductMapper类编写测试,则还应该模拟Adapter类在内部调用的Sql类的所有其他方法。

现在,您在$this->sql->prepareStatementForSqlObject($select);实例上调用Sql,该实例内部调用createStatement类的Adapter方法(您可以看到here on line 128 inside the Sql class)。但是在您的情况下,Adapter是一个模拟,这就是引发错误的原因:



因此,要解决此问题,您应该像对query方法所做的那样非常相似地模拟此方法:

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

在下一行中,您调用$statement->execute();,这意味着您还需要在execute中模拟$mockStatement方法。

如您所见,该测试变得非常繁琐。而且,您应该问自己是否在正确的道路上并测试正确的组件。您可以进行一些小的设计更改(改进),从而更轻松地测试ProductMapper类。

关于unit-testing - Zend Framework 2和PHPUnit-模拟Zend\Db\Adapter\Adapter类,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42133746/

10-13 06:13