Skip to content

Commit d9320f8

Browse files
committedJan 22, 2025·
minor #6758 Remove all mocks related to final classes (javiereguiluz)
This PR was squashed before being merged into the 4.x branch. Discussion ---------- Remove all mocks related to final classes These changes finally allow us to get rid of the ugly hack that we used to be able to mock final classes. 🥳 🎉 Commits ------- 7c9ef47 Remove all mocks related to final classes
2 parents 774d89f + 7c9ef47 commit d9320f8

File tree

5 files changed

+43
-116
lines changed

5 files changed

+43
-116
lines changed
 

‎phpunit.xml.dist

-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
<server name="KERNEL_CLASS" value="EasyCorp\Bundle\EasyAdminBundle\Tests\TestApplication\Kernel" />
1616
</php>
1717

18-
<extensions>
19-
<extension class="EasyCorp\Bundle\EasyAdminBundle\Test\PhpUnitExtension"/>
20-
</extensions>
21-
2218
<testsuites>
2319
<testsuite name="EasyAdmin Test Suite">
2420
<directory>tests/</directory>

‎src/Contracts/Field/FieldConfiguratorInterface.php

+3
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@ interface FieldConfiguratorInterface
1313
{
1414
public function supports(FieldDto $field, EntityDto $entityDto): bool;
1515

16+
/**
17+
* @param AdminContext $context This will change to AdminContextInterface in the next major version
18+
*/
1619
public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $context): void;
1720
}

‎src/Test/PhpUnitExtension.php

-44
This file was deleted.

‎tests/Field/AbstractFieldTest.php

+40-48
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22

33
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Field;
44

5+
use Doctrine\ORM\Mapping\ClassMetadata;
56
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
67
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
8+
use EasyCorp\Bundle\EasyAdminBundle\Config\Option\TextDirection;
79
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
10+
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Context\AdminContextInterface;
811
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
912
use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
1013
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
1114
use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
1215
use EasyCorp\Bundle\EasyAdminBundle\Dto\I18nDto;
1316
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
17+
use EasyCorp\Bundle\EasyAdminBundle\Registry\TemplateRegistry;
1418
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
1519
use Symfony\Component\HttpFoundation\Request;
1620

@@ -22,64 +26,52 @@ abstract class AbstractFieldTest extends KernelTestCase
2226

2327
protected function getEntityDto(): EntityDto
2428
{
25-
$entityDtoMock = $this->createMock(EntityDto::class);
26-
$entityDtoMock
27-
->expects($this->any())
28-
->method('getInstance')
29-
->willReturn(new class {});
29+
$reflectedClass = new \ReflectionClass(ClassMetadata::class);
30+
$classMetadata = $reflectedClass->newInstanceWithoutConstructor();
3031

31-
return $this->entityDto = $entityDtoMock;
32+
$reflectedClass = new \ReflectionClass(EntityDto::class);
33+
$entityDto = $reflectedClass->newInstanceWithoutConstructor();
34+
$instanceProperty = $reflectedClass->getProperty('instance');
35+
$instanceProperty->setValue($entityDto, new class {});
36+
$metadataProperty = $reflectedClass->getProperty('metadata');
37+
$metadataProperty->setValue($entityDto, $classMetadata);
38+
39+
return $this->entityDto = $entityDto;
3240
}
3341

34-
private function getAdminContext(string $pageName, string $requestLocale, string $actionName): AdminContext
42+
private function getAdminContext(string $pageName, string $requestLocale, string $actionName): AdminContextInterface
3543
{
3644
self::bootKernel();
3745

38-
$crudMock = $this->getMockBuilder(CrudDto::class)
39-
->disableOriginalConstructor()
40-
->onlyMethods(['getCurrentPage', 'getCurrentAction', 'getDatePattern', 'getDateTimePattern', 'getTimePattern'])
41-
->getMock();
42-
$crudMock->method('getCurrentPage')->willReturn($pageName);
43-
$crudMock->method('getCurrentAction')->willReturn($actionName);
44-
$crudMock->method('getDatePattern')->willReturn(DateTimeField::FORMAT_MEDIUM);
45-
$crudMock->method('getTimePattern')->willReturn(DateTimeField::FORMAT_MEDIUM);
46-
$crudMock->method('getDateTimePattern')->willReturn([DateTimeField::FORMAT_MEDIUM, DateTimeField::FORMAT_MEDIUM]);
46+
$crudDto = new CrudDto();
47+
$crudDto->setPageName($pageName);
48+
$crudDto->setCurrentAction($actionName);
49+
$crudDto->setDatePattern(DateTimeField::FORMAT_MEDIUM);
50+
$crudDto->setTimePattern(DateTimeField::FORMAT_MEDIUM);
51+
$crudDto->setDateTimePattern(DateTimeField::FORMAT_MEDIUM, DateTimeField::FORMAT_MEDIUM);
52+
53+
$i18Dto = new I18nDto($requestLocale, TextDirection::LTR, 'messages', []);
4754

48-
$i18nMock = $this->getMockBuilder(I18nDto::class)
49-
->disableOriginalConstructor()
50-
->onlyMethods(['getTranslationParameters', 'getTranslationDomain'])
51-
->getMock();
52-
$i18nMock->method('getTranslationParameters')->willReturn([]);
53-
$i18nMock->method('getTranslationDomain')->willReturn('messages');
55+
$reflectedClass = new \ReflectionClass(Request::class);
56+
$request = $reflectedClass->newInstanceWithoutConstructor();
57+
$instanceProperty = $reflectedClass->getProperty('locale');
58+
$instanceProperty->setValue($request, $requestLocale);
5459

55-
$requestMock = $this->getMockBuilder(Request::class)
56-
->disableOriginalConstructor()
57-
->onlyMethods(['getLocale'])
58-
->getMock();
59-
$requestMock->method('getLocale')->willReturn($requestLocale);
60+
$reflectedClass = new \ReflectionClass(TemplateRegistry::class);
61+
$templateRegistry = $reflectedClass->newInstanceWithoutConstructor();
6062

61-
$adminContextMock = $this->getMockBuilder(AdminContext::class)
62-
->disableOriginalConstructor()
63-
->onlyMethods(['getRequest', 'getCrud', 'getI18n', 'getTemplatePath'])
64-
->getMock();
65-
$adminContextMock
66-
->expects($this->any())
67-
->method('getRequest')
68-
->willReturn($requestMock);
69-
$adminContextMock
70-
->expects($this->any())
71-
->method('getCrud')
72-
->willReturn($crudMock);
73-
$adminContextMock
74-
->expects($this->any())
75-
->method('getI18n')
76-
->willReturn($i18nMock);
77-
$adminContextMock
78-
->expects($this->any())
79-
->method('getTemplatePath')
80-
->willReturn('@EasyAdmin/layout.html.twig'); // return any path to avoid injecting a TemplateRegistry
63+
$reflectedClass = new \ReflectionClass(AdminContext::class);
64+
$adminContext = $reflectedClass->newInstanceWithoutConstructor();
65+
$requestProperty = $reflectedClass->getProperty('request');
66+
$requestProperty->setValue($adminContext, $request);
67+
$crudDtoProperty = $reflectedClass->getProperty('crudDto');
68+
$crudDtoProperty->setValue($adminContext, $crudDto);
69+
$i18nDtoProperty = $reflectedClass->getProperty('i18nDto');
70+
$i18nDtoProperty->setValue($adminContext, $i18Dto);
71+
$templateRegistryProperty = $reflectedClass->getProperty('templateRegistry');
72+
$templateRegistryProperty->setValue($adminContext, $templateRegistry);
8173

82-
return $this->adminContext = $adminContextMock;
74+
return $this->adminContext = $adminContext;
8375
}
8476

8577
protected function configure(FieldInterface $field, string $pageName = Crud::PAGE_INDEX, string $requestLocale = 'en', string $actionName = Action::INDEX): FieldDto

‎tests/bootstrap.php

-20
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,6 @@
1313
// needed to avoid failed tests when other timezones than UTC are configured for PHP
1414
date_default_timezone_set('UTC');
1515

16-
// we want final classes in code but we need non-final classes in tests
17-
// after trying many solutions (see https://tomasvotruba.com/blog/2019/03/28/how-to-mock-final-classes-in-phpunit/)
18-
// none was reliable enough, so this custom solution removes the 'final' keyword
19-
// from the source code of all project files (and restore it when tests finish)
20-
// This has to be done BEFORE loading any PHP classes. Otherwise the changes in the
21-
// source code contents are ignored
22-
const EA_TEST_COMMENT_MARKER_START = '/* added-by-ea-tests';
23-
const EA_TEST_COMMENT_MARKER_END = '*/';
24-
25-
foreach (glob(__DIR__.'/../src/**/*.php') as $sourceFilePath) {
26-
$sourceFilePath = realpath($sourceFilePath);
27-
$sourceFileContents = file_get_contents($sourceFilePath);
28-
$sourceFileContentsWithoutFinalClasses = preg_replace(
29-
'/^final class (.*)$/m',
30-
sprintf('%s final %s class \1', EA_TEST_COMMENT_MARKER_START, EA_TEST_COMMENT_MARKER_END),
31-
$sourceFileContents
32-
);
33-
file_put_contents($sourceFilePath, $sourceFileContentsWithoutFinalClasses);
34-
}
35-
3616
$file = __DIR__.'/../vendor/autoload.php';
3717
if (!file_exists($file)) {
3818
throw new RuntimeException('Install dependencies using Composer to run the test suite.');

0 commit comments

Comments
 (0)
Please sign in to comment.