foundation/tests/Unit/Core/DependencyInjection/InflectableContainerTest.php
2025-06-13 18:29:55 +02:00

210 lines
No EOL
6.6 KiB
PHP

<?php
declare(strict_types = 1);
namespace Foundation\Tests\Unit\Core\DependencyInjection;
use DI\Container;
use Foundation\Core\DependencyInjection\AttributeProcessorInterface;
use Foundation\Core\DependencyInjection\InflectableContainer;
use PHPUnit\Framework\TestCase;
class InflectableContainerTest extends TestCase
{
private Container $phpDiContainer;
private InflectableContainer $container;
protected function setUp(): void {
$this->phpDiContainer = new Container();
$this->container = new InflectableContainer($this->phpDiContainer);
}
public function testCanGetAndSetServices(): void {
$this->container->set('test.value', 'hello world');
self::assertTrue($this->container->has('test.value'));
self::assertSame('hello world', $this->container->get('test.value'));
}
public function testCanMakeInstancesWithParameters(): void {
$instance = $this->container->make(TestClass::class, ['value' => 'test']);
self::assertInstanceOf(TestClass::class, $instance);
self::assertSame('test', $instance->getValue());
}
public function testCanRegisterClassWithoutDependencies(): void {
$instance = $this->container->register(TestClass::class);
self::assertInstanceOf(TestClass::class, $instance);
self::assertTrue($this->container->has(TestClass::class));
}
public function testCanRegisterClassWithDependencies(): void {
$this->container->set('dependency', new TestDependency('injected'));
$instance = $this->container->register(TestClassWithDependency::class, ['dependency']);
self::assertInstanceOf(TestClassWithDependency::class, $instance);
self::assertSame('injected', $instance->getDependencyValue());
}
public function testCanBindInterfaceToImplementation(): void {
$instance = $this->container->bind(TestInterface::class, TestImplementation::class);
self::assertInstanceOf(TestImplementation::class, $instance);
self::assertTrue($this->container->has(TestInterface::class));
$retrieved = $this->container->get(TestInterface::class);
self::assertInstanceOf(TestImplementation::class, $retrieved);
}
public function testCanBindInterfaceToImplementationWithDependencies(): void {
$this->container->set('dependency', new TestDependency('bound'));
$instance = $this->container->bind(
TestInterface::class,
TestImplementationWithDependency::class,
['dependency'],
);
self::assertInstanceOf(TestImplementationWithDependency::class, $instance);
self::assertSame('bound', $instance->getDependencyValue());
}
public function testInflectionSystemAppliesDirectClassInflections(): void {
$this->container->registerInflection(
TestInflectionTarget::class,
'setInjectedValue',
['injected_value'],
);
$instance = $this->container->make(TestInflectionTarget::class);
self::assertSame('injected_value', $instance->getInjectedValue());
}
public function testInflectionSystemAppliesInterfaceInflections(): void {
$this->container->registerInflection(
TestInflectionInterface::class,
'setInjectedValue',
['interface_value'],
);
$instance = $this->container->make(TestInflectionImplementation::class);
self::assertSame('interface_value', $instance->getInjectedValue());
}
public function testInflectionSystemResolvesParametersFromContainer(): void {
$dependency = new TestDependency('resolved');
$this->container->set(TestDependency::class, $dependency);
$this->container->registerInflection(
TestInflectionTarget::class,
'setDependency',
[TestDependency::class],
);
$instance = $this->container->make(TestInflectionTarget::class);
self::assertSame($dependency, $instance->getDependency());
}
public function testAttributeProcessorsAreCalledDuringRegistration(): void {
$processor = $this->createMock(AttributeProcessorInterface::class);
$processor->expects(self::once())->method('canProcess')->with(TestClass::class)->willReturn(true);
$processor->expects(self::once())->method('process')->with(TestClass::class);
$this->container->addAttributeProcessor($processor);
$this->container->register(TestClass::class);
}
public function testReturnsNonObjectsDirectly(): void {
$this->container->set('array.value', ['test' => 'array']);
$this->container->set('string.value', 'test string');
self::assertSame(['test' => 'array'], $this->container->get('array.value'));
self::assertSame('test string', $this->container->get('string.value'));
}
}
class TestClass
{
public function __construct(private readonly string $value = 'default') {}
public function getValue(): string {
return $this->value;
}
}
class TestDependency
{
public function __construct(private readonly string $value) {}
public function getValue(): string {
return $this->value;
}
}
class TestClassWithDependency
{
public function __construct(private readonly TestDependency $dependency) {}
public function getDependencyValue(): string {
return $this->dependency->getValue();
}
}
interface TestInterface { }
class TestImplementation implements TestInterface { }
class TestImplementationWithDependency implements TestInterface
{
public function __construct(private readonly TestDependency $dependency) {}
public function getDependencyValue(): string {
return $this->dependency->getValue();
}
}
interface TestInflectionInterface
{
public function setInjectedValue(string $value): void;
}
class TestInflectionTarget
{
private ?string $injectedValue = null;
private ?TestDependency $dependency = null;
public function setInjectedValue(string $value): void {
$this->injectedValue = $value;
}
public function getInjectedValue(): ?string {
return $this->injectedValue;
}
public function setDependency(TestDependency $dependency): void {
$this->dependency = $dependency;
}
public function getDependency(): ?TestDependency {
return $this->dependency;
}
}
class TestInflectionImplementation implements TestInflectionInterface
{
private ?string $injectedValue = null;
public function setInjectedValue(string $value): void {
$this->injectedValue = $value;
}
public function getInjectedValue(): ?string {
return $this->injectedValue;
}
}