I’m writing tests for a Symfony 4.4 app and I need to mock a service named TokenService
which is a dependency of some classes that I need to test (repositories, services), but I’m not sure how to proceed to pass some dependencies via DI :
self::$container->get('App\Services\classToTest') // Not able to pass dependencies ?
Am I forced to instantiate the class and do something like this :
$classToTest = new \App\Services\classToTest($depencencyOne,$tokenMock,...)
Also, I need to mock only one of the dependencies, so do I have to pass all the other dependencies anyway ?
>Solution :
You can get()
from the testing service container but you can also set()
!
I can give a real example I recently had where I needed to replace the HttpClient with its mock implementation in some tests:
// Here I replace the service with a mock implementation (a PHPUnit mock can work too)
self::getContainer()->set(HttpClientInterface::class, new MockHttpClient([
new MockResponse('{"some":"JSON"}'),
new MockResponse('{"other":"JSON"}'),
]));
// Here $myService will get the mock injected instead of the original HTTP Client
$myService = self::getContainer()->get(MyService::class);
But it should be noted that I had to make the service public to be allowed to overwrite it:
# services.yaml
when@test:
services:
Symfony\Contracts\HttpClient\HttpClientInterface:
alias: '.debug.http_client'
public: true
If you don’t (or can’t) replace a dependency in the service container, then yes you will need to instantiate the tested service yourself and get every dependency:
$myService = new MyService(
self::getContainer()->get(RealService::class),
$someMockDependency
);
If the issue comes from your own code, be sure to follow good practices to make your code easier to test (mostly dependency injection and dependency inversion principles).