Skip to content

[TesterBundle] Fix AutowireServiceMock broken by Symfony 6.4.34 TestContainer::set() change#336

Merged
kiloumap merged 1 commit intomasterfrom
fix/autowire-service-mock-test-container-set
Mar 9, 2026
Merged

[TesterBundle] Fix AutowireServiceMock broken by Symfony 6.4.34 TestContainer::set() change#336
kiloumap merged 1 commit intomasterfrom
fix/autowire-service-mock-test-container-set

Conversation

@kiloumap
Copy link
Copy Markdown
Collaborator

@kiloumap kiloumap commented Mar 9, 2026

Summary

Symfony 6.4.34 refactored TestContainer::set() from a try/catch pattern to a proactive check, which broke mock injection for all services registered in the test private services locator.

Old behavior (Symfony 6.4.31 — worked)

public function set(string $id, mixed $service): void
{
    $container = $this->getPublicContainer();
    $renamedId = $this->renamedIds[$id] ?? $id;

    try {
        $container->set($renamedId, $service);  // stores in $container->services
    } catch (InvalidArgumentException $e) {
        if (!str_starts_with($e->getMessage(), "The \"$renamedId\" service is private")) {
            throw $e;
        }
        $container->privates[$renamedId] = $service;  // only reached on exception
    }
}

Container::set() was called first, storing the mock in $container->services. The fallback to $container->privates only happened if an exception was thrown (which it wasn't for these services).

New behavior (Symfony 6.4.34 — broken)

public function set(string $id, mixed $service): void
{
    $container = $this->getPublicContainer();
    $renamedId = $this->renamedIds[$id] ?? $id;

    if (!$this->getPrivateContainer()->has($renamedId)) {
        $container->set($renamedId, $service);
    } else {
        $container->privates[$renamedId] = $service;  // always reached in test env
    }
}

The new code checks getPrivateContainer()->has() first. In the test environment, all services are registered in test.private_services_locator, so mocks always go to $container->privates — never reaching $container->services.

Impact

Compiled service factories only resolve dependencies from $container->services, so they never see the mocks and create real service instances instead. This causes issues like infinite loops with real HTTP calls leading to OOM (6GB+ memory exhausted).

Fix

Bypass TestContainer::set() entirely by calling Container::set() directly on the public container (accessed via reflection on TestContainer::getPublicContainer()). This ensures mocks are stored in $container->services where compiled factories can find them.

…ontainer::set() change

Symfony 6.4.34 refactored TestContainer::set() from a try/catch pattern
to a proactive check using getPrivateContainer()->has(). In the old code
(v6.4.31), Container::set() was called first, storing the mock in
$container->services. It only fell back to $container->privates if an
exception was thrown.

In v6.4.34, the new code checks getPrivateContainer()->has() first. In
the test environment, ALL services are registered in the private services
locator, so mocks always end up in $container->privates — never reaching
$container->services.

Compiled service factories only resolve dependencies from
$container->services, so they never see the mocks and create real service
instances instead. This causes issues like infinite loops with real HTTP
calls leading to OOM (6GB memory exhausted).

The fix bypasses TestContainer::set() entirely by calling Container::set()
directly on the public container via reflection on
TestContainer::getPublicContainer(). This ensures mocks are stored in
$container->services where compiled factories can find them.
@kiloumap kiloumap force-pushed the fix/autowire-service-mock-test-container-set branch from 2be70be to e767a59 Compare March 9, 2026 21:57
@kiloumap kiloumap merged commit fb9dfa6 into master Mar 9, 2026
31 checks passed
@kiloumap kiloumap deleted the fix/autowire-service-mock-test-container-set branch March 9, 2026 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant