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

3
我正在编写一个基本的PDO包装类,当我想要模拟使用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;
    }
}

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

我也尝试用->will($this->throwException($pdoException))替换->willThrowException($pdoException),但它不起作用。
我注意到,如果我使用->willThrowException(new \PDOException('Message from PDOException'))替换->willThrowException($pdoException),那么它可以工作,但是这样就要依赖于PDOException类而不是模拟它。
有什么想法吗?

有点儿天真,但声明的顺序会有影响吗?你告诉它抛出pdoExceptionMock,但在将其分配给pdoMock之后再填充数据。 - Steven Scott
1个回答

5

只有两个声明:

1) PHP 5.x 中的所有异常都扩展自基本 Exception,并且它将 'getMessage' 方法定义为final:

final public string Exception::getMessage ( void )

2) 当您尝试模拟final方法时,PHPUnit会默默地什么都不做(您可以查看生成模拟代码这里canMockMethod对于final方法返回false)。

因此

->setMethods(['getMessage'])

没有影响。

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


1
在这种特定情况下,不模拟异常没有问题。然而,在像 Guzzle 这样具有多个参数的情况下,就会出现问题。例如 RequestException:public function __construct( $message, RequestInterface $request, ResponseInterface $response = null, \Exception $previous = null, array $handlerContext = [] - Nikola Petkanski
我没意识到它是最终版。谢谢你澄清了这一点。 - Bishal Paudel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接