使PHP方法在测试中抛出异常

时间:2016-03-13 02:10:39

标签: php mocking phpunit integration-testing php-7

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

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

我的测试是一个集成测试,可以从工厂中获取EntityManager

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

有没有一种很好的方式来编写我之后的测试?能够在EntityManager实例上修补相关方法将是完美的,但这需要runkit extension

我使用的是PHP 7,如果需要可能可以切换到PHP 7.1。如果您感到好奇,code is on GitHub,相关测试位于DoctrineAuthorizationUpdaterTest {3}}

1 个答案:

答案 0 :(得分:1)

您可以在测试命名空间中创建一个代理类,而不是通过PHPUnit模拟API创建模拟对象,该代理类从工厂获取EntityManager作为构造函数参数。代理类将每个方法调用传递给EntityManager,但要抛出异常的调用除外。大多数调用都可以使用__call魔术方法传递,但调用Doctrine\Common\Persistence\ObjectManager的接口方法除外,Proxy类可能必须实现(interface methods can't be implemented with call)。

这个代理类是"复杂的假#34;你在谈论并想避免?那么也许你可以使用Mockery library而不必编写课程。