Hub 20xx
Hub 20xx

Reputation: 461

PHPUnit: Mocking PDOException->getMessage() method

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

Answers (1)

Nikita U.
Nikita U.

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

Related Questions