Maico Orazio
Posted on October 4, 2023
In Symfony 3.4, they made all services private by default, which means you can no longer call $this->get('my_service_id')
in your controllers to quickly obtain a service.
This change was made because direct usage of services from the container is considered a bad practice. That's why controllers allow services to be injected using Type Hinting in their methods and constructors.
The only remaining inconvenience is that when running tests, we get the following:
> bin/phpunit
# Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The "App\Service\S" service or alias has
# been removed or inlined when the container was compiled. You should either make it public, or stop using the container
# directly and use dependency injection instead.
Problem
We want to create a service and test it before integrating it with the rest of the project (standard TDD approach).
I have a repository R
that implements the RInterface
interface. The RInterface
interface is used in the S
service (type hinted constructor). The S
service is used in the C
controller (again, as a constructor parameter).
When we run the test for the S
service...
class STest extends KernelTestCase
{
public function testGetItems(): void
{
self::bootKernel();
$container = self::$kernel->getContainer();
$service = $container->get(S::class);
// ... other
}
}
We see the following message:
> bin/phpunit
# Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The "App\Service\S" service or alias has
# been removed or inlined when the container was compiled. You should either make it public, or stop using the container
# directly and use dependency injection instead.
Reason
The service can be integrated when certain conditions are met.
To check if the S
service is present in the test container, we type the following command:
> bin/console debug:container 'App\Service\S' --env=test
Here is the message we will see:
Information for Service "App\Core\Service\S"
=========================================================
---------------- ---------------------------------
Option Value
---------------- ---------------------------------
Service ID App\Service\S
Class App\Service\S
Tags -
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
---------------- ---------------------------------
As mentioned in the received message when running the test, the S
service appears as private (Public: no
). It's important to note that due to how the Symfony container works, unused services are removed from the container. This means that if you have a private service not used by any other service, Symfony removes it, and you cannot retrieve it from the container.
Solution
The solution is to explicitly define the S
service as public
so that Symfony does not remove it. The most appropriate solution would be to create a public alias only in the test environment for the service you want to test.
# config/services_test.yaml
services:
test_alias.service:s:
alias: 'App\Service\S'
public: true
Good work 👨💻
Posted on October 4, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.