Files
michaelschiemer/tests/Unit/Framework/DI/ContainerTest.php
Michael Schiemer c8b47e647d feat(Docker): Upgrade to PHP 8.5.0RC3 with native ext-uri support
BREAKING CHANGE: Requires PHP 8.5.0RC3

Changes:
- Update Docker base image from php:8.4-fpm to php:8.5.0RC3-fpm
- Enable ext-uri for native WHATWG URL parsing support
- Update composer.json PHP requirement from ^8.4 to ^8.5
- Add ext-uri as required extension in composer.json
- Move URL classes from Url.php85/ to Url/ directory (now compatible)
- Remove temporary PHP 8.4 compatibility workarounds

Benefits:
- Native URL parsing with Uri\WhatWg\Url class
- Better performance for URL operations
- Future-proof with latest PHP features
- Eliminates PHP version compatibility issues
2025-10-27 09:31:28 +01:00

182 lines
5.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\DI;
use App\Framework\DI\DefaultContainer;
class TestService
{
public function __construct(public string $message = 'Hello World')
{
}
}
class DependentService
{
public function __construct(public TestService $service)
{
}
}
interface TestInterface
{
public function getMessage(): string;
}
class TestImplementation implements TestInterface
{
public function getMessage(): string
{
return 'Implementation';
}
}
test('container can create simple class without dependencies', function () {
$container = new DefaultContainer();
$service = $container->get(TestService::class);
expect($service)->toBeInstanceOf(TestService::class);
expect($service->message)->toBe('Hello World');
});
test('container resolves dependencies automatically', function () {
$container = new DefaultContainer();
$service = $container->get(DependentService::class);
expect($service)->toBeInstanceOf(DependentService::class);
expect($service->service)->toBeInstanceOf(TestService::class);
expect($service->service->message)->toBe('Hello World');
});
test('container can bind interfaces to implementations', function () {
$container = new DefaultContainer();
$container->bind(TestInterface::class, TestImplementation::class);
$service = $container->get(TestInterface::class);
expect($service)->toBeInstanceOf(TestImplementation::class);
expect($service->getMessage())->toBe('Implementation');
});
test('container can bind with closures', function () {
$container = new DefaultContainer();
$container->bind(TestService::class, function () {
return new TestService('Custom Message');
});
$service = $container->get(TestService::class);
expect($service->message)->toBe('Custom Message');
});
test('container can register singletons', function () {
$container = new DefaultContainer();
// Use instance() for true singleton behavior in tests
$instance = new TestService('Singleton Message');
$container->instance(TestService::class, $instance);
$service1 = $container->get(TestService::class);
$service2 = $container->get(TestService::class);
expect($service1)->toBe($service2); // Same instance
expect($service1->message)->toBe('Singleton Message');
});
test('container can store instances directly', function () {
$container = new DefaultContainer();
$instance = new TestService('Direct Instance');
$container->instance(TestService::class, $instance);
$retrieved = $container->get(TestService::class);
expect($retrieved)->toBe($instance);
expect($retrieved->message)->toBe('Direct Instance');
});
test('container has method works correctly', function () {
$container = new DefaultContainer();
expect($container->has(TestService::class))->toBeTrue(); // Can be auto-wired
expect($container->has('NonExistentClass'))->toBeFalse();
// Use interface binding instead of string identifier
$container->bind(TestInterface::class, TestImplementation::class);
expect($container->has(TestInterface::class))->toBeTrue();
});
test('container forget removes bindings', function () {
$container = new DefaultContainer();
// Use class-based binding instead of string identifier
$container->bind(TestInterface::class, TestImplementation::class);
expect($container->has(TestInterface::class))->toBeTrue();
$container->forget(TestInterface::class);
expect($container->has(TestInterface::class))->toBeFalse();
});
test('container can get service ids', function () {
$container = new DefaultContainer();
// Use class-based identifiers
$container->bind(TestInterface::class, TestImplementation::class);
$container->bind(DependentService::class, DependentService::class);
$serviceIds = $container->getServiceIds();
// Container should report bindings
expect($serviceIds)->toContain(TestInterface::class);
expect($serviceIds)->toContain(DependentService::class);
expect(count($serviceIds))->toBeGreaterThanOrEqual(2);
});
test('container can flush all bindings', function () {
$container = new DefaultContainer();
// Use class-based identifiers
$container->bind(TestInterface::class, TestImplementation::class);
$container->get(TestInterface::class); // Instantiate to ensure in instances
$serviceIdsBefore = $container->getServiceIds();
$countBefore = count($serviceIdsBefore);
// Before flush
expect($container->has(TestInterface::class))->toBeTrue();
$container->flush();
// After flush, most services should be removed
$serviceIdsAfter = $container->getServiceIds();
$countAfter = count($serviceIdsAfter);
// Flush should reduce service count significantly
expect($countAfter)->toBeLessThan($countBefore);
expect($serviceIdsAfter)->not->toContain(TestInterface::class);
});
class InvokerTestService
{
public function method(TestService $service): string
{
return $service->message;
}
}
test('container method invoker works', function () {
$container = new DefaultContainer();
$service = new InvokerTestService();
$result = $container->invoker->invokeOn($service, 'method');
expect($result)->toBe('Hello World');
});