Overriding the Request and Testing

Viewed 421

I have an issue with extending the default Request class from Symfony and writing unit tests.

I set a request factory in index.php after that set some properties to the custom Request in a RequestListener and it works fine, but when I run a test class, it breaks.

Here is the index.php:

<?php

use App\Kernel;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;

require dirname(__DIR__).'/vendor/autoload.php';

(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');

if ($_SERVER['APP_DEBUG']) {
    umask(0000);

    Debug::enable();
}

$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
Request::setFactory(function (array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null) {
    return new \App\Extension\HttpFoundationExtension\Request($query, $request, $attributes, $cookies, $files, $server, $content);
});
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Here is also the test class:

<?php

namespace App\Tests\Api;

use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;

class AccountTest extends ApiTestCase
{
    public function testGetCollection(): void
    {
        $response = static::createClient()->request("GET", "/accounts");
        self::assertResponseStatusCodeSame(201);
    }
}

I don't know why, but if I extend the WebTestCase and not the ApiTestCase class from the test, the index.php is started.


I found the following info in Symfony docs, but I don't know how this can help me. :)

To run your functional tests, the WebTestCase class needs to know which is the application kernel to bootstrap it. The kernel class is usually defined in the KERNEL_CLASS environment variable (included in the default .env.test file provided by Symfony): If your use case is more complex, you can also override the createKernel() or getKernelClass() methods of your functional test, which take precedence over the KERNEL_CLASS env var.



Here are some useful links I found on this topic:
  • Testing and the Kernel: Click here to view
  • Overriding the Request: Click here to view


EDIT:

Here is my tests/bootstrap.php:

<?php

use Symfony\Component\Dotenv\Dotenv;

require dirname(__DIR__).'/vendor/autoload.php';

if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
    require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
    (new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}

Here is phpunit.xml.dist:

<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="bin/.phpunit/phpunit.xsd"
         backupGlobals="false"
         colors="true"
         bootstrap="tests/bootstrap.php"
>
    <php>
        <ini name="error_reporting" value="-1" />
        <server name="APP_ENV" value="test" force="true" />
        <server name="SHELL_VERBOSITY" value="-1" />
        <server name="SYMFONY_PHPUNIT_REMOVE" value="" />
        <server name="SYMFONY_PHPUNIT_VERSION" value="8.5" />
    </php>

    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">src</directory>
        </whitelist>
    </filter>

    <listeners>
        <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
    </listeners>

    <!-- Run `composer require symfony/panther` before enabling this extension -->
    <!--
    <extensions>
        <extension class="Symfony\Component\Panther\ServerExtension" />
    </extensions>
    -->
</phpunit>

Here is part of .env.test:

KERNEL_CLASS='App\Kernel'

With this configuration I still have the same issue!

Thanks!

1 Answers

Check your .env.test file and search for a KERNEL_CLASS declaration. If not present add it (ex: KERNEL_CLASS='App\Kernel').

.env.test:

KERNEL_CLASS='App\Kernel'

Then check that the bootstrap test file is loading the .env file using the Dotenv component (and check that the dotenv component is installed). This file is usually located at tests/bootstrap.php.

<?php

use Symfony\Component\Dotenv\Dotenv;

require \dirname(__DIR__).'/vendor/autoload.php';

if (\file_exists(\dirname(__DIR__).'/config/bootstrap.php')) {
    require \dirname(__DIR__).'/config/bootstrap.php';
} elseif (\method_exists(Dotenv::class, 'bootEnv')) {
    (new Dotenv())->bootEnv(\dirname(__DIR__).'/.env');
}

Verify that the phpunit.xml (or phpunit.xml.dist) is requiring it via the bootstrap attribute of the root phpunit tag.

<?xml version="1.0" encoding="UTF-8"?>
<phpunit ... bootstrap="tests/bootstrap.php">
    ...
</phpunit>

Your error should be gone now.

I don't think that request override plays a role in your error. Anyway, it is documented in code that setting the request factory is left only for BC. You can just call createFromGlobals from your extended Request class to have exactly the same result.

Related