Reputation: 411
I have a class which handles errors, including exceptions. If an exception is caught, I will pass the exception as an argument to my exception/error handler.
try {
someTrowingFnc();
} catch (\Exception $e) {
this->error->exception($e);
}
Now I want to unit test this error handler and mock the exception.
I am finding it hard to mock the exception so that I can control the exception message, file and line.
$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');
The results of the code below always returns NULL
$file = $exception->getFile();
$line = $exception->getLine();
$msg = $exception->getMessage();
Is there a work-around to mock exceptions or am I just doing something wrong?
Upvotes: 6
Views: 6594
Reputation: 2991
The throwException() in PHPUnit TestCase class can take any instance of Throwable as param.
Here is an example that should pass if you have try/catch in FileWriterToBeTested and will fail if you do not have 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'));
tested class sample:
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);
}
}
}
Upvotes: 0
Reputation: 6676
It's a bit of a hack, but try adding something like this to your 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);
}
Now you can change the values.
$exception = $this->getMock('Exception');
$this->setObjectAttribute($exception, 'file', '/file/name');
$this->setObjectAttribute($exception, 'line', 3069);
$this->setObjectAttribute($exception, 'message', 'Error test');
Of course, you haven't really mocked the class, though this can still be useful if you have a more complex custom Exception. Also you won't be able to count how many times the method is called, but since you were using $this->any()
, I assume that doesn't matter.
It's also useful when you're testing how an Exception is handled, for example to see if another method (such as a logger) was called with the the exception message as a parameter
Upvotes: 2
Reputation: 1032
The Exception class methods that return the error details such as getFile()
etc are defined/declared as final
methods. And this is one limitation of PHPUnit currently in mocking methods that are protected, private, and 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.
As seen here: https://phpunit.de/manual/current/en/test-doubles.html
Upvotes: 5