Mocking final methods

866 Views Asked by At

Sometimes I need to mock or stub a class that has a final method. I know that you should have an interface on such a class, but if the class is in a library that is not maintained by me, how would I go about?

Let's say, I have made a custom configureContainer method in the Kernel of my Symfony project. I want to test it, but it calls $container->import() several times. Now I would want to stub ContainerConfigurator, and let $container->import just return void. Otherwise it would just complaint about import being used before it was initialized or something like that.

But when I put this in my test:

   $stubbedContainerConfigurator = $this->createStub(ContainerConfigurator::class);
   $stubbedContainerConfigurator->method('import');

It will complaint about import being final and unable to be stubbed or mocked.

Does anyone know how to properly test this?

1

There are 1 best solutions below

0
Nicolai Fröhlich On

You can use dg/bypass-finals to allow stubs for final classes.

Installation:

composer require dg/bypass-finals --dev

Usage: Create a PHPUnit hook (i.e. src/PHPUnit/Hook/BypassFinalsHook.php):

<?php 

declare(strict_types=1);

use DG\BypassFinals;
use PHPUnit\Runner\BeforeTestHook;

namespace App\PHPUnit\Hooks;

final class BypassFinalHook implements BeforeTestHook
{
    public function executeBeforeTest(string $test): void
    {
        BypassFinals::enable();
    }
}

Register the hook in your phpunit.xml.dist configuration:

<phpunit bootstrap="vendor/autoload.php">
    <extensions>
        <extension class="App\PHPUnit\Hook\BypassFinalHook"/>
    </extensions>
</phpunit>