Reputation: 461
I am writing a basic PDO wrapper class and when I want to simulate the throwing of an exception by PDOStatement::prepare()
using willThrowException()
with the mock of PDOException
in my unit test, the returned value of getMessage()
is always and empty string instead of what I set up.
Here is how I tried it:
// WrapperClass.php
<?php
class WrapperClass
{
private $pdo;
private $error = '';
public function __construct(\PDO $pdo)
{
$this->pdo = $pdo;
}
public function save()
{
$sql = 'INSERT INTO ...';
try {
$this->pdo->prepare($sql);
// some value binding and executing the statement
} catch (\PDOException $pdoException) {
$this->error = $pdoException->getMessage();
}
}
public function getError()
{
return $this->error;
}
}
and my test:
// WrapperClassTest.php
<?php
class WrapperClassTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function save_saves_PDOException_message_in_error_property()
{
$pdoMock = $this->getMockBuilder('WrapperClass')
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
$pdoMock->expects($this->once())
->method('prepare')
->willThrowException($pdoExceptionMock);
$pdoExceptionMock = $this->getMockBuilder('\PDOException')
->setMethods(['getMessage'])
->getMock();
$message = 'Message from PDOException';
$pdoExceptionMock->expects($this->once())
->method('getMessage')
->willReturn($message);
$wrapperClass = new WrapperClass($pdoMock);
$wrapperClass->save();
$this->assertEquals($message, $wrapperClass->getError());
}
}
I also tried to replace ->willThrowException($pdoException)
with ->will($this->throwException($pdoException))
but it does not work.
I noticed that if I replace ->willThrowException($pdoException)
with ->willThrowException(new \PDOException('Message from PDOException'))
it works but then I'm relying on the PDOException
class instead of mocking it.
Any ideas?
Upvotes: 3
Views: 1408
Reputation: 3618
Just 2 statements:
1) All exceptions in PHP 5.x extends base Exception and it defines 'getMessage' method as final:
final public string Exception::getMessage ( void )
2) PHPUnit silently do nothing when you try to mock final methods (you can see code that generate mocks here, canMockMethod
returns false for final methods)
So
->setMethods(['getMessage'])
has no effect.
On the other side you don't really need to mock exceptions because they are value objects. Passing new PDOException('Message from PDOException')
is pretty ok.
Upvotes: 5