Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 681
Hub 20xx Avatar asked May 25 '26 04:05

Hub 20xx


1 Answers

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.

like image 77
Nikita U. Avatar answered May 26 '26 17:05

Nikita U.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!