使 PHP 方法在测试中引发异常


Making a PHP method throw an exception in a test

我有一个AuthorizationUpdater服务,其实现基于Doctrine ORM。当我完成生产代码并进行一些测试时,由于不知道如何编写这两个代码,我被迫退出了通常的TDD周期:

  • testWhenDatabaseReadFails_exceptionIsThrown(测试此代码)
  • testWhenDatabaseWriteFails_exceptionIsThrown(测试此代码)

AuthorizationUpdater的 Doctrine 实现在其构造函数中采用 Doctine EntityManager,它用于执行读取和写入。这两个测试应该测试当这些 EntityManager 方法中的任何一个抛出 Doctine ORMException 时,它会被服务捕获并转换为域异常。

我的测试是一个集成测试,它从工厂获得EntityManager使用。

在这种情况下,通过PHPUnit模拟API(getMock('className')->method('updateStuff')->willThrow(new SomeExcetion))创建一个简单的假货似乎并不好。该服务在抛出异常的方法之前使用该EntityManager,因此无法模拟所有内容。这留下了一些复杂的EntityManager伪造并部分模拟它(只是应该引发异常的方法)。后者需要提供真正的构造函数参数,我的测试实际上无法访问这些参数,因为此责任在另一个包中。因此,这两种方法都不令人满意,让我宁愿不测试这种ORMException捕获行为。

有没有一种好方法可以编写我想要的测试?能够在EntityManager实例上对相关方法进行猴子修补将是完美的,尽管这需要 runkit 扩展。

我使用的是 PHP 7,

如果需要,也许可以切换到 PHP 7.1。如果您好奇,代码在 GitHub 上,相关测试驻留在 DoctrineAuthorizationUpdaterTest 的末尾。

与其通过 PHPUnit 模拟 API 创建模拟对象,不如在测试命名空间中创建一个代理类,该类从工厂获取EntityManager作为构造函数参数。代理类将每个方法调用传递给EntityManager,除了要抛出异常的调用。大多数调用都可以使用 __call magic 方法传递,除了对 Doctrine'Common'Persistence'ObjectManager 接口方法的调用,Proxy 类可能必须实现(接口方法不能用 call 实现)。

这个代理类是你正在谈论的"复杂的假货",想要避免吗?然后,也许您可以使用Mockery库而不必编写类。