Matthieu Napoli
Matthieu Napoli

Reputation: 49533

How to set parameters or services in Symfony in PHPUnit tests?

We are using PHPUnit to test parts of our application. In some tests, we want to change the value of a parameter or override a service (but only for that test, not for all tests).

What is the recommended way to configure Symfony's container on the fly in tests?

The problem we have met is that the container doesn't recompile itself when config is set on the fly (because it only recompiles itself when files have changed).

Upvotes: 7

Views: 9120

Answers (3)

Odin
Odin

Reputation: 3358

As of Symfony 5, there is a possible shortcut. As long as all you need is to set parameters, you can simply change them in the $_ENV variable. I.e. the following works

services.yaml

services:
    App\Controller\ApiController:
        arguments:
            $param: '%env(my_param)%'

ApiController.php

class ApiController extends AbstractController
{
    public function __construct(string $param)
    {
        $this->param = $param;
    }
    ...
}

test

class ApiControllerTest extends WebTestCase
{
    public function testSomething(): void
    {
        $_ENV['param'] = 'my-value';
        $client = self::createClient();
        $client->request(...)
    }
    ...
}

Upvotes: 2

Matthieu Napoli
Matthieu Napoli

Reputation: 49533

This is how we proceed for now:

class TestKernel extends \AppKernel
{
    public function __construct(\Closure $containerConfigurator, $environment = 'test', $debug = false)
    {
        $this->containerConfigurator = $containerConfigurator;

        parent::__construct($environment, $debug);
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        parent::registerContainerConfiguration($loader);
        $loader->load($this->containerConfigurator);
    }

    /**
     * Override the parent method to force recompiling the container.
     * For performance reasons the container is also not dumped to disk.
     */
    protected function initializeContainer()
    {
        $this->container = $this->buildContainer();
        $this->container->compile();
        $this->container->set('kernel', $this);
    }
}

Then we have added this method in our PHPUnit base test class:

/**
 * Rebuilds the container with custom container configuration.
 *
 * @param \Closure $containerConfigurator Closure that takes the ContainerBuilder and configures it.
 *
 * Example:
 *
 *     $this->rebuildContainer(function (ContainerBuilder $container) {
 *         $container->setParameter('foo', 'bar');
 *     });
 */
public function rebuildContainer(\Closure $containerConfigurator) : ContainerInterface
{
    if ($this->kernel) {
        $this->kernel->shutdown();
        $this->kernel = null;
        $this->container = null;
    }

    $this->kernel = new TestKernel($containerConfigurator);
    $this->kernel->boot();
    $this->container = $this->kernel->getContainer();

    return $this->container;
}

Upvotes: 5

mblaettermann
mblaettermann

Reputation: 1943

You do not test the container or config in Unit Tests. In Unit Tests the goal is to test the units encapsulated without the full application stack.

For functional tests the recommended way is to edit it in the inherited config under app/config/config_test.yml

All values from config_dev.yml can be overridden there.

Upvotes: -4

Related Questions