So, after build the container, container's immutable and you can't add other services/parameters to it (replace too, because this contain two operation - delete and set).
But, in more cases we should have any opportunities for replace logic inside a our service.
For this, I use custom services only for tests. This service override default service (class TestUserService extends UserService
) or use interfaces (better). After create class I declare this service with same name in app/config/test.yml (when@test)
or via another opportunities.
namespace Acme;
interface UserServiceInterface
{
public function create(object $some): User;
}
/* Original service */
readonly class UserService implements UserServiceInterface
{
public function create(object $some): User
{
// Some actions here
}
}
/* Service only for test */
class TestUserService implements UserServiceInterface
{
private ?\Closure $callback;
public function shouldExecute(\Closure $callback): void
{
$this->callback = $callback;
}
public function create(object $some): User
{
if ($this->callback) {
return ($this->callback)($some);
}
throw new \RuntimeException(\sprintf(
'The service %s wrong configured. Please call "shouldExecute" method previously.',
__CLASS__
));
}
}
And declare it in you config (YAML as an example):
services:
user_service:
class: Acme\UserService
when@test:
services:
user_service:
class: Acme\TestUserService
After this, I can easy use any logic inside UserService::create
only for test environment.
class SomeTest extends TestCase
{
private TestUserService $userService;
protected function setUp(): void
{
$this->userService = self::getContainer()->get('user_service');
}
public function shouldSuccess(): void
{
$this->userService->shouldExecute(static function (object $some) {
self::assertEquals('foo bar', $some);
return new User();
});
// logic for testing
}
}
In few cases, developer want to have opportunities to change logic only in specific cases. For this we can use decorator pattern and call origin UserService
inside TestUserService
if callback not configured.
$container = static::$kernel->getContainer();
– Anderson$this->container->getDefinition('user.user_service')->setSynthetic(true);
before doing your$container->set
call. – Breve