如何编写抛出PDOException的单元测试

时间:2017-03-16 13:53:31

标签: php unit-testing pdo phpunit

我有一个类来处理各种数据库异常,例如死锁和序列化事务失败。我正试图对它进行单元测试并遇到障碍。我要测试的代码示例:

public function run(callable $callable)
{
    $this->beginTransaction();
    try {
        $callable();
        $this->commit();
    } catch (\PDOException $e) {
        if ($e->getCode() == '40P01' || $e->getCode() == '55P03') {
            // Deadlock (40P01) or lock not available (55P03).
            ...
        } 
        ...
    }
}

PDOException的文档说开发人员不应该自己抛出它,今天我在尝试编写单元测试时发现了原因:

$obj->run(function() {
    ...
    throw new \PDOException('Deadlock', '40P01');
});

A non well formed numeric value encounteredPDOExceptionException中断合同,因为异常代码必须为int,PDOException创建字符串代码以匹配SQL标准。它们应该创建一个单独的SQLCode属性,而是重用内置代码。因此,在单元测试中抛出一个带有实际SQL错误代码的PDOException是不可能的。

接下来我试图模仿PDOException

class PDOExceptionMock extends \PDOException
{
    protected $code = '';

    public function setCode($code)
    {
        $this->code = $code;
    }

    // This won't work because getCode is final
    public function getCode()
    {
        return $this->code;
    }
}

这不会编译因为'无法覆盖最终方法Exception-> getCode()'。

由于我不能(并且不想)使用真实数据库重新创建每种类型的死锁和事务错误,如何编写单元测试哪些需要抛出PDOException?< /强>

1 个答案:

答案 0 :(得分:1)

您不需要覆盖getCode。它已经存在,所以你只需要设置代码。例如。

class A {
    public function run(callable $callable)
    {
        try {
            $callable();
        } catch (\PDOException $e) {
            if ($e->getCode() == '40P01' || $e->getCode() == '55P03') {
                return 'expected';
            } 
            return 'unexpected';
        }
        return 'no caught';
    }
}

class StubException extends \PDOException {
    public function __construct() {
        parent::__construct();
        $this->code = '40P01';
    }
}

$a = new A;

echo $a->run(function(){throw new StubException;});

回应&#39;预期&#39;;