From 31c3ac4e501d4d34cfa606cbe5d3bc82e8defc79 Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Mon, 27 May 2024 13:41:52 +0200 Subject: [PATCH] make better testable --- autoload/functions_oxDIC.php | 2 + composer.json | 3 +- d3DicHandler.php | 69 +++---- tests/phpunit.xml | 5 + tests/unit/autoload/functions_oxDICTest.php | 2 + tests/unit/d3DicExceptionTest.php | 50 +++++ tests/unit/d3DicHandlerTest.php | 210 ++++++++++++++++---- tests/unit/d3DicUtilitiesTest.php | 2 + tests/unit/definitionFileContainerTest.php | 2 +- 9 files changed, 256 insertions(+), 89 deletions(-) create mode 100644 tests/unit/d3DicExceptionTest.php diff --git a/autoload/functions_oxDIC.php b/autoload/functions_oxDIC.php index 1631be6..701fa17 100644 --- a/autoload/functions_oxDIC.php +++ b/autoload/functions_oxDIC.php @@ -33,7 +33,9 @@ function d3GetOxidDIC(): Container { try { return d3GetOxidDIC_withExceptions(); + // @codeCoverageIgnoreStart } catch (d3DicException $exception) { trigger_error($exception->getMessage(), E_USER_ERROR); } + // @codeCoverageIgnoreEnd } diff --git a/composer.json b/composer.json index c2ed5a2..2b065f4 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ "phpunit/phpunit": "^9.6", "friendsofphp/php-cs-fixer": "~3.13.0", "phpstan/phpstan": "^1.10", - "rector/rector": "^0.18.13" + "rector/rector": "^0.18.13", + "mikey179/vfsstream": "^1.6.8" }, "autoload": { "psr-4": { diff --git a/d3DicHandler.php b/d3DicHandler.php index 66b7646..e961abc 100644 --- a/d3DicHandler.php +++ b/d3DicHandler.php @@ -40,24 +40,7 @@ class d3DicHandler implements d3DicHandlerInterface */ public static function getInstance(): Container { - try { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - $caller = $trace[1]; - $functionName = $caller['function']; - - if (in_array(strtolower($functionName), array_map('strtolower', self::$circularReferenceMethodNames))) { - throw oxNew(Exception::class, 'method ' . $functionName . " can't use DIC due the danger of circular reference"); - } - - if (null == self::$_instance) { - $oDicHandler = oxNew(d3DicHandler::class); - self::$_instance = $oDicHandler->buildContainer(); - } - } catch (Exception $exception) { - throw new d3DicException($exception); - } - - return self::$_instance; + return oxNew(d3DicHandler::class)->createInstance(); } /** @@ -66,17 +49,25 @@ class d3DicHandler implements d3DicHandlerInterface */ public static function getUncompiledInstance(): Container { - try { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - $caller = $trace[1]; - $functionName = $caller['function']; + return oxNew(d3DicHandler::class)->createInstance(false); + } + public static function removeInstance(): void + { + self::$_instance = null; + } + + public function createInstance(bool $compiled = true): Container + { + try { + $functionName = $this->getFunctionNameFromTrace(); if (in_array(strtolower($functionName), array_map('strtolower', self::$circularReferenceMethodNames))) { - throw oxNew(Exception::class, 'method '.$functionName." can't use DIC due the danger of circular reference"); + throw oxNew(Exception::class, 'method ' . $functionName . " can't use DIC due the danger of circular reference"); } - $oDicHandler = oxNew(d3DicHandler::class); - self::$_instance = $oDicHandler->buildContainer(false); + if (null == self::$_instance) { + self::$_instance = $this->buildContainer($compiled); + } } catch (Exception $exception) { throw new d3DicException($exception); } @@ -84,9 +75,11 @@ class d3DicHandler implements d3DicHandlerInterface return self::$_instance; } - public static function removeInstance(): void + protected function getFunctionNameFromTrace() { - self::$_instance = null; + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $caller = $trace[1]; + return $caller['function']; } public function d3GetConfig(): Config @@ -135,11 +128,6 @@ class d3DicHandler implements d3DicHandlerInterface } } - protected function isNotInTest(): bool - { - return false == defined('OXID_PHP_UNIT') || true == defined('D3_MODCFG_TEST'); - } - protected function cacheFileExists(): bool { return file_exists($this->d3GetCacheFilePath()); @@ -162,11 +150,8 @@ class d3DicHandler implements d3DicHandlerInterface if ($compileAndDump) { $container->compile(); - - if ($this->isNotInTest()) { - $dumper = new PhpDumper($container); - file_put_contents($this->d3GetCacheFilePath(), $dumper->dump(['class' => 'd3DIContainerCache'])); - } + $dumper = $this->getPhpDumper($container); + file_put_contents($this->d3GetCacheFilePath(), $dumper->dump(['class' => 'd3DIContainerCache'])); } } @@ -181,7 +166,6 @@ class d3DicHandler implements d3DicHandlerInterface return $config->isProductiveMode() && !$config->getConfigParam('iDebug') - && $this->isNotInTest() && $this->cacheFileExists(); } @@ -190,13 +174,8 @@ class d3DicHandler implements d3DicHandlerInterface return oxNew(ContainerBuilder::class); } - public function __clone() + public function getPhpDumper(ContainerBuilder $containerBuilder): PhpDumper { - /** keep clear */ - } - - public function __construct() - { - /** keep clear */ + return new PhpDumper( $containerBuilder); } } diff --git a/tests/phpunit.xml b/tests/phpunit.xml index f8f7b77..d2f8dce 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -19,6 +19,11 @@ ../ + + ./ + ../rector.php + ../.php-cs-fixer.php + diff --git a/tests/unit/autoload/functions_oxDICTest.php b/tests/unit/autoload/functions_oxDICTest.php index eb11539..490a105 100644 --- a/tests/unit/autoload/functions_oxDICTest.php +++ b/tests/unit/autoload/functions_oxDICTest.php @@ -28,6 +28,7 @@ class functions_oxDICTest extends TestCase /** * @test * @throws Exception + * @covers ::d3GetOxidDIC_withExceptions() */ public function d3GetOxidDIC_withExceptionsTest(): void { @@ -42,6 +43,7 @@ class functions_oxDICTest extends TestCase /** * @test * @throws Exception + * @covers ::d3GetOxidDIC() */ public function d3GetOxidDICTest(): void { diff --git a/tests/unit/d3DicExceptionTest.php b/tests/unit/d3DicExceptionTest.php new file mode 100644 index 0000000..a4a4e0b --- /dev/null +++ b/tests/unit/d3DicExceptionTest.php @@ -0,0 +1,50 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\DIContainerHandler\tests; + +use D3\DIContainerHandler\d3DicException; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +class d3DicExceptionTest extends TestCase +{ + /** + * @test + * @return void + * @covers \D3\DIContainerHandler\d3DicException::__construct + */ + public function canConstruct() + { + $previousMessage = 'previousMessage'; + $previousCode = 123; + $previous = new InvalidArgumentException($previousMessage, $previousCode); + + $exception = new d3DicException($previous); + + $this->assertSame( + $previous, + $exception->getPrevious() + ); + $this->assertSame( + $previousMessage, + $exception->getMessage() + ); + $this->assertSame( + $previousCode, + $exception->getCode() + ); + } +} \ No newline at end of file diff --git a/tests/unit/d3DicHandlerTest.php b/tests/unit/d3DicHandlerTest.php index 4c39d1c..04819fa 100644 --- a/tests/unit/d3DicHandlerTest.php +++ b/tests/unit/d3DicHandlerTest.php @@ -15,23 +15,33 @@ declare(strict_types=1); namespace D3\DIContainerHandler\tests; +use D3\DIContainerHandler\d3DicException; use D3\DIContainerHandler\d3DicHandler; use D3\TestingTools\Development\CanAccessRestricted; use d3DIContainerCache; +use Exception; use Generator; +use org\bovigo\vfs\vfsStream; use OxidEsales\Eshop\Core\Config; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionException; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -define('D3_MODCFG_TEST', true); - class d3DicHandlerTest extends TestCase { use CanAccessRestricted; + public function setUp(): void + { + parent::setUp(); + + d3DicHandler::removeInstance(); + } + /** * @test * @throws ReflectionException @@ -118,6 +128,65 @@ class d3DicHandlerTest extends TestCase ); } + /** + * @test + * @param bool $throwException + * @param bool $expectException + * @param string $circularReferenceMethod + * + * @return void + * @throws ReflectionException + * @covers \D3\DIContainerHandler\d3DicHandler::createInstance + * @dataProvider canCreateInstanceDataProvider + */ + public function canCreateInstance(bool $throwException, bool $expectException, string $circularReferenceMethod = '') + { + /** @var d3DicHandler|MockObject $sut */ + $sut = $this->getMockBuilder(d3DicHandler::class) + ->onlyMethods(['buildContainer', 'getFunctionNameFromTrace']) + ->getMock(); + if ($throwException) + $sut->method( 'buildContainer' )->willThrowException( new Exception( 'fixture' ) ); + + $sut->method('getFunctionNameFromTrace')->willReturn($circularReferenceMethod); + if ($expectException) + $this->expectException(d3DicException::class); + + $this->callMethod( + $sut, + 'createInstance' + ); + } + + public function canCreateInstanceDataProvider(): Generator + { + yield "don't throw exception" => [false, false]; + yield "throw exception" => [true, true]; + yield "has circular reference method name" => [false, true, 'getViewConfig']; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\DIContainerHandler\d3DicHandler::getFunctionNameFromTrace + */ + public function canGetFunctionNameFromTrace() + { + /** @var d3DicHandler|MockObject $sut */ + $sut = $this->getMockBuilder(d3DicHandler::class) + ->onlyMethods(get_class_methods(d3DicHandler::class)) + ->getMock(); + + $this->assertSame( + 'invokeArgs', + $this->callMethod( + $sut, + 'getFunctionNameFromTrace' + ) + ); + } + /** * @test * @throws ReflectionException @@ -225,23 +294,6 @@ class d3DicHandlerTest extends TestCase ); } - /** - * @test - * @throws ReflectionException - * @covers \D3\DIContainerHandler\d3DicHandler::isNotInTest - */ - public function isNotInTest(): void - { - $sut = new d3DicHandler(); - - $this->assertTrue( - $this->callMethod( - $sut, - 'isNotInTest' - ) - ); - } - /** * @test * @throws ReflectionException @@ -276,52 +328,102 @@ class d3DicHandlerTest extends TestCase /** * @test + * @param bool $useCacheContainer + * @param bool $compile * - * + * @return void * @throws ReflectionException * @dataProvider buildContainerTestDataProvider * @covers \D3\DIContainerHandler\d3DicHandler::buildContainer */ - public function buildContainerTest(bool $productive, int $debug, bool $notInTest, bool $cacheFileExist, bool $cachedContainer): void + public function buildContainerTest(bool $useCacheContainer, bool $compile): void { - $cachedContainerMock = $this->getMockBuilder(d3DIContainerCache::class) - ->getMock(); + $structure = [ + 'source_directory' => [], + ]; + vfsStream::setup(); + $fsRoot = vfsStream::create($structure); $containerBuilderMock = $this->getMockBuilder(ContainerBuilder::class)->onlyMethods([ 'compile' ])->getMock(); - $containerBuilderMock->expects($this->exactly((int) ! $cachedContainer))->method('compile'); + $containerBuilderMock->expects($this->exactly((int) (!$useCacheContainer && $compile)))->method('compile'); - $configMock = $this->getMockBuilder(Config::class) - ->onlyMethods(['isProductiveMode', 'getConfigParam']) + /** @var PhpDumper|MockObject $phpDumperMock */ + $phpDumperMock = $this->getMockBuilder(PhpDumper::class) + ->disableOriginalConstructor() + ->onlyMethods(get_class_methods(PhpDumper::class)) ->getMock(); - $configMock->method('isProductiveMode')->willReturn($productive); - $configMock->method('getConfigParam')->willReturnMap([['iDebug', $debug]]); + $phpDumperMock->expects($this->exactly((int) (!$useCacheContainer && $compile)))->method('dump'); + /** @var d3DicHandler|MockObject $sut */ $sut = $this->getMockBuilder(d3DicHandler::class) - ->onlyMethods(['d3GetConfig', 'd3GetCacheContainer', 'getContainerBuilder', 'isNotInTest', 'cacheFileExists']) + ->onlyMethods(['d3UseCachedContainer', 'd3GetCacheContainer', 'getContainerBuilder', 'd3GetCacheFilePath', 'getPhpDumper']) ->getMock(); - $sut->method('d3GetConfig')->willReturn($configMock); - $sut->expects($this->exactly((int) $cachedContainer))->method('d3GetCacheContainer')->willReturn($cachedContainerMock); - $sut->expects($this->exactly((int) !$cachedContainer))->method('getContainerBuilder')->willReturn($containerBuilderMock); - $sut->method('isNotInTest')->willReturn($notInTest); - $sut->method('cacheFileExists')->willReturn($cacheFileExist); + $sut->expects($this->once())->method('d3UseCachedContainer')->willReturn($useCacheContainer); + $sut->expects($this->exactly((int) $useCacheContainer))->method('d3GetCacheContainer'); + $sut->expects($this->exactly((int) !$useCacheContainer))->method('getContainerBuilder')->willReturn($containerBuilderMock); + $sut->method('d3GetCacheFilePath')->willReturn($fsRoot->getChild('source_directory')->path().'/DIContainer.php'); + $sut->method('getPhpDumper')->willReturn($phpDumperMock); - $this->assertSame( - $cachedContainer ? $cachedContainerMock : $containerBuilderMock, + $this->assertInstanceOf( + Container::class, $this->callMethod( $sut, 'buildContainer', - ['false'] + [$compile] ) ); } public function buildContainerTestDataProvider(): Generator { - yield 'notProductive' => [false, 0, false, true, false]; - yield 'debug' => [true, 1, false, true, false]; - yield 'inTest' => [true, 0, false, true, false]; - yield 'cacheFileNotExist' => [true, 0, false, false, false]; - yield 'cachedContainer' => [true, 0, true, true, true]; + yield "can't use cached container, do compile" => [false, true]; + yield "can't use cached container, don't compile" => [false, false]; + yield "use cached container" => [true, false]; + } + + /** + * @test + * @param bool $productive + * @param int $debug + * @param bool $cacheFileExist + * @param bool $expected + * + * @return void + * @throws ReflectionException + * @covers \D3\DIContainerHandler\d3DicHandler::d3UseCachedContainer + * @dataProvider canUseCachedContainerDataProvider + */ + public function canUseCachedContainerTest(bool $productive, int $debug, bool $cacheFileExist, bool $expected) + { + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['isProductiveMode', 'getConfigParam']) + ->getMock(); + $configMock->method('isProductiveMode')->willReturn($productive); + $configMock->method('getConfigParam')->willReturnMap([['iDebug', NULL, $debug]]); + + /** @var d3DicHandler|MockObject $sut */ + $sut = $this->getMockBuilder(d3DicHandler::class) + ->onlyMethods(['d3GetConfig', 'cacheFileExists']) + ->getMock(); + $sut->method('d3GetConfig')->willReturn($configMock); + $sut->method('cacheFileExists')->willReturn($cacheFileExist); + + $this->assertSame( + $expected, + $this->callMethod( + $sut, + 'd3UseCachedContainer' + ) + ); + } + + public function canUseCachedContainerDataProvider(): Generator + { + yield "not productive" => [false, 0, true, false]; + yield 'is debug' => [true, 1, true, false]; + yield 'no cache file' => [true, 0, false, false]; + yield 'can use cached' => [true, 0, true, true]; } /** @@ -341,4 +443,28 @@ class d3DicHandlerTest extends TestCase ) ); } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\DIContainerHandler\d3DicHandler::getPhpDumper + */ + public function canGetPhpDumper(): void + { + /** @var ContainerBuilder|MockObject $containerBuilderMock */ + $containerBuilderMock = $this->getMockBuilder(ContainerBuilder::class) + ->onlyMethods(['isCompiled']) + ->getMock(); + $containerBuilderMock->method('isCompiled')->willReturn(true); + + $this->assertInstanceOf( + PhpDumper::class, + $this->callMethod( + new d3DicHandler(), + 'getPhpDumper', + [$containerBuilderMock] + ) + ); + } } diff --git a/tests/unit/d3DicUtilitiesTest.php b/tests/unit/d3DicUtilitiesTest.php index 77492e4..a24b5b9 100644 --- a/tests/unit/d3DicUtilitiesTest.php +++ b/tests/unit/d3DicUtilitiesTest.php @@ -29,7 +29,9 @@ class d3DicUtilitiesTest extends TestCase /** * @test * + * @param string $className * @param string|null $additional + * @param string $expected * * @throws ReflectionException * @covers \D3\DIContainerHandler\d3DicUtilities::getServiceId diff --git a/tests/unit/definitionFileContainerTest.php b/tests/unit/definitionFileContainerTest.php index 2d26b89..aa783b1 100644 --- a/tests/unit/definitionFileContainerTest.php +++ b/tests/unit/definitionFileContainerTest.php @@ -29,10 +29,10 @@ class definitionFileContainerTest extends TestCase /** * @test * - * * @throws ReflectionException * @dataProvider addDefinitionsTestDataProvider * @covers \D3\DIContainerHandler\definitionFileContainer::addDefinitions + * @covers \D3\DIContainerHandler\definitionFileContainer::__construct */ public function addDefinitionsTest(string $file, string $type, int $sumand, bool $expectException): void {