7

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).

2
  • you could try something like $this->client->getContainer()->set('application.facebook_guzzle', $client); sometimes this don't work... Commented Feb 1, 2017 at 16:04
  • 3
    @Matteo this doesn't work with parameters (because the container is compiled) Commented Feb 1, 2017 at 16:53

3 Answers 3

6

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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Is that still the way to go or is there another approach? I use Symfony 5 and try to adopt the code to it, f. ex. $this->kernel is no self::$kernel. While doing this I was wondering if there is now a more straight forward method.. tbh, I couldn't find any. Still thankful for this nice piece of code here!
2

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(...)
    }
    ...
}

Comments

-4

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.

6 Comments

And how would this help on a test by test basis?
For Unit Testing you should not rely on the Container/Config at all. For functional tests with Symfony your are using PhpUnit, too. This is the right way to do it: symfony.com/doc/current/testing.html#functional-tests
Have you even read the question? The fact that PHPunit is being used does not imply a unit test. And your proposed solution is nonsense. Down voted accordingly.
@Cerad: agreed. @mblaettermann: in the question I said I don't want to put that in config_test.yml because I want to change the parameter/service only for one test.
Well @Cerad, what is the right solution, then? The whole test setup seems like a mess. What is even the usecase to swap out parameters for a single test? Either you do UnitTesting => No Container. Integration testing: Same Container/Config for every Testcase. Anything else is just a broken testing suite or even broken application design.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.