Reputation: 15394
I have a class which handles a variety of database exceptions such as deadlocks and serialized transaction failures. I'm trying to unit test it and ran into a roadblock. An example of the code I want to test:
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).
...
}
...
}
}
The documentation for PDOException
says developers should not throw it themselves, and today I found out why when trying to write a unit test:
$obj->run(function() {
...
throw new \PDOException('Deadlock', '40P01');
});
A non well formed numeric value encountered
. PDOException
breaks the contract with Exception
because exception codes must be int and PDOException
creates string codes to match SQL standards. They should have made a separate SQLCode property but instead reused the built-in code. Therefore it's impossible to throw a PDOException
with a real SQL error code within a unit test.
Next I tried to mock 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;
}
}
This won't compile because 'Cannot override final method Exception->getCode()'.
Since I can't (and don't want to) recreate every type of deadlock and transaction error using a real database, how can one write unit tests which need to throw PDOException
s?
Upvotes: 1
Views: 858
Reputation: 37128
You don't need to overwrite getCode
. It is already there, so you need just to set the code. E.g.
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;});
echoes 'expected';
Upvotes: 2