PHPUnit模拟异常

时间:2014-09-12 21:57:35

标签: exception error-handling phpunit

我有一个处理错误的类,包括异常。如果捕获到异常,我会将异常作为参数传递给我的异常/错误处理程序。

try {
    someTrowingFnc();
} catch (\Exception $e) {
    this->error->exception($e);
}

现在我想对这个错误处理程序进行单元测试并模拟异常。

我发现很难模拟异常,因此我可以控制异常消息,文件和行。

$exceptionMock = $this->getMock('Exception', array(
    'getFile',
    'getLine',
    'getMessage',
    'getTrace'
)); // Tried all mock arguments like disable callOriginalConstructor

$exceptionMock->expects($this->any())
    ->method('getFile')
    ->willReturn('/file/name');

$exceptionMock->expects($this->any())
    ->method('getLine')
    ->willReturn('3069');

$exceptionMock->expects($this->any())
    ->method('getMessage')
    ->willReturn('Error test');

以下代码的结果始终返回NULL

$file   = $exception->getFile();
$line   = $exception->getLine();
$msg    = $exception->getMessage();

是否存在模拟异常的解决方法,或者我只是做错了什么?

3 个答案:

答案 0 :(得分:5)

返回错误详细信息(如getFile()等)的Exception类方法被定义/声明为final方法。这是PHPUnit目前在模拟受保护,私有和最终方法的一个限制。

Limitations
Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.

如下所示:https://phpunit.de/manual/current/en/test-doubles.html

答案 1 :(得分:0)

这有点像黑客攻击,但尝试在TestCase中添加类似的东西:

/**
 * @param object $object        The object to update
 * @param string $attributeName The attribute to change
 * @param mixed  $value         The value to change it to
 */
protected function setObjectAttribute($object, $attributeName, $value)
{
    $reflection = new \ReflectionObject($object);
    $property = $reflection->getProperty($attributeName);
    $property->setAccessible(true);
    $property->setValue($object, $value);
}

现在您可以更改值。

$exception = $this->getMock('Exception');
$this->setObjectAttribute($exception, 'file',    '/file/name');
$this->setObjectAttribute($exception, 'line',    3069);
$this->setObjectAttribute($exception, 'message', 'Error test');

当然,你还没有真的嘲笑这个类,但如果你有一个更复杂的自定义异常,这仍然有用。此外,您将无法计算调用该方法的次数,但由于您使用的是$this->any(),我认为这无关紧要。

当您测试如何处理异常时,它也很有用,例如,查看是否使用异常消息作为参数调用了另一个方法(例如记录器)

答案 2 :(得分:0)

PHPUnit TestCase类中的throwException()可以将Throwable的任何实例作为参数。

这是一个示例,如果您在FileWriterToBeTested中进行了try / catch,应该通过,而如果您没有try / catch,则将失败:

    $this->reader = $this->getMockBuilder(Reader::class)->getMock();
    $this->reader->method('getFile')->will(static::throwException(new \Exception()));
    $file = new FileWriterToBeTested($this->reader);
    static::assertNull($file->getFile('someParamLikePath'));

经过测试的课程示例:

class FileWriterToBeTested
{

    /**
     * @var Reader
     */
    private $reader;

    public function __construct(Reader $reader): void
    {
        $this->reader = $reader;
    }

    /**
     * @return Reader
     */
    public function getFile(string $path): void
    {
        try {
            $this->reader->getFile($path);
        } catch (\Exception $e) {
            $this->error->exception($e);
        }        
    }

}