Throwing a \Doctrine\DBAL\Driver\DriverException in a unit test mock

454 views Asked by At

Redacting unit tests, I am confronted to this problem. A piece of code that I want to test catches a \Doctrine\DBAL\Exception\RetryableException. The first constructor in the classes chain is the one of DriverException and is built like this :

/**
 * @param string                                $message         The exception message.
 * @param \Doctrine\DBAL\Driver\DriverException $driverException The DBAL driver exception to chain.
 */
public function __construct($message, \Doctrine\DBAL\Driver\DriverException $driverException)
{
    $exception = null;

    if ($driverException instanceof Exception) {
        $exception = $driverException;
    }

    parent::__construct($message, 0, $exception);

    $this->driverException = $driverException;
}

I feel like I am confronted to the problem of the egg and the chicken, here. How can I instanciante a class that takes an instance of itself as mandatory argument in the first place ?

2

There are 2 answers

0
Moonchild On

Note: I won't mark this auto-response as a solution, it is more a workaround.

Instead of throwing the right exception in my unit test mock, I have created a simpler one, extending Exception but still implementing the original interface RetryableException, as it's the interface that is caught in the code I am testing. While not being what I wanted to do, it does the job in my precise case.

0
michel v On

Here is how I have an actual instance of DriverException in my unit tests, using an anonymous class instead of a mock:

<?php

declare(strict_types=1);

use Doctrine\DBAL\Driver\Exception as TheDriverException;
use Doctrine\DBAL\Exception\DriverException;
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    // ... the rest of the test case

    private function getDriverExceptionWithCode(int $code): DriverException
    {
        $theDriverException = new class($code) extends \Exception implements TheDriverException {
            public function __construct(int $code)
            {
                parent::__construct('oh no, you broke it :(', $code);
            }

            public function getSQLState(): ?string
            {
                return null;
            }
        };

        return new DriverException($theDriverException, null);
    }
}

In my case I needed to unit test a situation where the code is catching a DriverException with a specific code, but you can extend the code as you wish, or make it simpler. The only thing you need is to implement getSQLState, after all.

Hope this helps whoever stumbles on this question from their favorite search engine.