【问题标题】:Testing Doctrine EntityManager write failures测试 Doctrine EntityManager 写入失败
【发布时间】:2018-07-31 09:49:46
【问题描述】:

在我的 PHP 应用程序中,我稍后在数据访问中使用 Doctrine ORM。在这一层中,我有一些服务,例如通常使用 Doctrines EntityManager 的存储库。在这些服务中,进行某种形式修改的方法通常遵循以下模式:

public function modifyStuff( /* ... */ ) {
    try {
        $stuff = $this->entityManager->find( /* ... */ )
    }
    catch ( ORMException $ex ) {
        /* ... */
    }

    // Poke at $stuff

    try {
        $this->entityManager->persist( $stuff );
        $this->entityManager->flush();
    }
    catch ( ORMException $ex ) {
        /* code that needs to be tested */
    }
}

我正在尝试找到一种方法来测试第二个 catch 块中的代码:处理写入失败的代码。所以在我的测试中,我需要在写东西时让 EntityManager 抛出。当然,我想在我的测试中尽量减少对这个存储库的实现(即使用哪些学说方法)和 EntityManager 接口和实现本身的绑定。理想情况下我可以做类似的事情

$entityManager = new TestEntityManager();
$entityManager->throwOnWrite();

之后,EntityManager 将正常运行,但它会在写入时抛出。 (我的存储库有这样的测试替身。)

我尝试使用 PHPUnit mock API 如下:

$entityManager = $this->getMockBuilder( EntityManager::class )->disableOrgninalConstructor()->getMock()
$entityManager->expects( $this->any() )
    ->method( 'persist' )
    ->willThrowException( new ORMException() );

这并不理想,因为现在我的测试绑定到 persist 方法,尽管这不是什么大问题。这不起作用,因为服务要运行它的构造函数需要一些参数。然后我尝试了

$entityManager =
    $this->getMockBuilder( EntityManager::class )
        ->setConstructorArgs( [
            $this->entityManager->getConnection(),
            $this->entityManager->getConfiguration(),
            $this->entityManager->getEventManager()
        ] )
        ->getMock();

发现 EntityManager 的构造函数是不公开的。所以看来我将无法使用 PHPUnit 模拟 API。

关于如何让 EntityManager 在写入时抛出,或者以其他方式测试应该处理这种情况的代码的任何想法?

【问题讨论】:

  • 可能是stackoverflow.com/a/43083956/5769763?作为评论发布,因为我没有时间尝试实际验证这是您的意思以及是否有效。
  • 这种方法是不可能的,因为服务需要它的构造函数参数,而 PHPUnit 显然无法将构造函数参数填充到非公共构造函数中。

标签: php doctrine-orm phpunit


【解决方案1】:

您可以通过从onFlush 事件处理程序中抛出此类异常来模拟写入失败。考虑这个例子:

$p = new Entity\Product();
$p->setName("Test Product");
$em->persist($p);
$em->flush();

$em->getEventManager()->addEventListener(\Doctrine\ORM\Events::onFlush, new WriteErrorListener());


// Nothing changed, no exceptions.
$p->setName("Test Product");
$em->flush();

try {
    $p->setName("Name changed");
    $em->flush();
    echo "\n\n    UNEXPECTED\n\n";
} catch (\Doctrine\ORM\ORMException $ex) {
    echo $ex->getMessage() . "\n";
}

WriteErrorListener:

use \Doctrine\ORM\Query\QueryException;

class WriteErrorListener 
{
    public function onFlush(\Doctrine\ORM\Event\OnFlushEventArgs $eventArgs)
    {
        $em = $eventArgs->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            // You can inspect $entity and provide additional information
            throw new QueryException('[Test condition] Insertion of entity pending');
        }

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            throw new QueryException('[Test condition] Update of entity pending');
        }
        // And so on...
    }
}

运行:

$ php app.php 
[Test condition] Update of entity pending

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-10
  • 2015-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多