PHPUnit:模拟 PDOException->getMessage() 方法

PHPUnit: Mocking PDOException->getMessage() method

我正在编写一个基本的 PDO 包装器 class,当我想通过 PDOStatement::prepare() 使用 willThrowException()PDOException 的模拟来模拟抛出异常时我的单元测试,getMessage() 的返回值始终是空字符串,而不是我设置的值。

这是我的尝试方式:

// 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;
    }
}

和我的测试:

// 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());
    }
}

我也试过用->will($this->throwException($pdoException))替换->willThrowException($pdoException),但是没用。

我注意到,如果我将 ->willThrowException($pdoException) 替换为 ->willThrowException(new \PDOException('Message from PDOException')) 它会起作用,但我依赖 PDOException class 而不是模拟它。

有什么想法吗?

只有 2 个语句:

1) PHP 5.x 中的所有异常都将基本异常和 it defines 'getMessage' 方法扩展为最终的:

final public string Exception::getMessage ( void )

2) PHPUnit 在您尝试模拟 final 方法时静默不执行任何操作(您可以看到为 final 生成模拟 herecanMockMethod returns false 的代码方法)

所以

->setMethods(['getMessage'])

没有效果。

另一方面,您实际上不需要模拟异常,因为它们是值对象。通过new PDOException('Message from PDOException')就可以了。