PHPUnit Mock 异常

6

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

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();

是否有一种方法可以模拟异常,或者我做错了什么?


1
你的对象是否抛出异常? 否则,你可以生成一个模拟数据,但如果没有任何异常抛出,或者你不生成那个条件,那么这个模拟就不会发生。 能否显示所有代码,或者至少是类的代码?我自2011年以来就没再用过phpunit,所以请注意,但是我能想起来的是,你有一个装饰器来捕获预期的异常,但是你的情况似乎有些不同,你正在生成一个mock,但(这是我的假设),你没有生成抛出异常本身的条件,所以你的断言会失败。 - Jorge Omar Vázquez
3个回答

5

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

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/zh_cn/test-doubles.html


我认为你可以创建一个虚拟类来实现你的异常类。在该函数中,当被调用时,你可以明确地抛出一个错误,这样你就可以知道确切的文件、行号等信息。使用测试用例调用该函数并捕获明确抛出的异常,断言文件、行号等是否正确。 - antoniovassell

2

这是一种比较巧妙的方法,但您可以尝试在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(),我假设这并不重要。
当你测试异常处理方式时,它也非常有用,例如查看另一个方法(如记录器)是否以异常消息作为参数被调用。

0

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

这里有一个示例,如果你在FileWriterToBeTested中添加了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);
        }        
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接