我有一个处理错误的类,包括异常。如果捕获到异常,我会将异常作为参数传递给我的异常/错误处理程序。
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();
是否存在模拟异常的解决方法,或者我只是做错了什么?
答案 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.
答案 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);
}
}
}