Skip to content

Commit

Permalink
[TASK] Use phpunit attributes in functional tests
Browse files Browse the repository at this point in the history
phpunit 11 deprecates annotations like `@test` and
`@dataProvider` in favor of their attribute counterparts.

We'll adapt core main & v12 to keep v12 backports
simple. The patch takes care of Tests/Functional and
Tests/FunctionalDeprecated.

Script `Build/Scripts/splitFunctionalTests.php` is
adapted to deal with annotations for CI to continue
splitting functional tests into chunks. This also
fixes detection in two test cases that had unexpected
`@test` annotation combinations which were not properly
detected before.

> composer req --dev rector/rector
> wget https://forge.typo3.org/attachments/download/38273/rector.php
> find typo3/ -name \*Test.php | grep Tests/Functional | xargs bin/rector process
> rm rector.php
> composer rem --dev rector/rector

Minor manual adaption in a few files plus cgl fixes.

Also deny `@test` annotion in annotationChecker.php now
to not introduce new occurences with other patches anymore.
The script will receive another cleanup to look at further
obsolete annotations. `@dataProvider` is currently still
used in acceptance tests.

Change-Id: I42705b57193a32db6fe17276d53476ecddcae835
Resolves: #103195
Related: #103180
Releases: main, 12.4
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83115
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
  • Loading branch information
lolli42 committed Feb 25, 2024
1 parent 4ad2e75 commit c51afbf
Show file tree
Hide file tree
Showing 501 changed files with 4,697 additions and 11,135 deletions.
2 changes: 1 addition & 1 deletion Build/Scripts/annotationChecker.php
Expand Up @@ -54,7 +54,7 @@ public function enterNode(Node $node)
// PHPDocumentor 2 tags
'api', 'author', 'category', 'copyright', 'deprecated', 'example', 'filesource', 'global', 'ignore', 'internal', 'license', 'link', 'method', 'package', 'param', 'property', 'property-read', 'property-write', 'return', 'see', 'since', 'source', 'subpackage', 'throws', 'todo', 'TODO', 'usedby', 'uses', 'var', 'version',
// PHPUnit tags
'codeCoverageIgnore', 'codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'test', 'dataProvider', 'group', 'skip', 'depends', 'expectedException', 'before', 'requires',
'codeCoverageIgnore', 'codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'dataProvider', 'group', 'skip', 'depends', 'expectedException', 'before', 'requires',
// codeception tags
'env',
// PHPCheckStyle
Expand Down
49 changes: 21 additions & 28 deletions Build/Scripts/splitFunctionalTests.php
Expand Up @@ -15,7 +15,6 @@
* The TYPO3 project - inspiring people to share!
*/

use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
Expand Down Expand Up @@ -189,18 +188,16 @@ class FunctionalTestCaseVisitor extends NodeVisitorAbstract
/**
* @var array[] An array of arrays with test method names and optionally a data provider name
*/
private $tests = [];
private array $tests = [];

/**
* @var string Fully qualified test class name
*/
private $fqcn;
private string $fqcn;

/**
* Create a list of '@test' annotated methods in a test case
* file and see if single tests use data providers.
*
* @param Node $node
*/
public function enterNode(Node $node): void
{
Expand All @@ -211,36 +208,34 @@ public function enterNode(Node $node): void
$this->fqcn = (string)$node->namespacedName;
}

if ($node instanceof Node\Stmt\ClassMethod
&& ($docComment = $node->getDocComment()) instanceof Doc
) {
preg_match_all(
'/\s*\s@(?<annotations>[^\s.].*)\n/',
$docComment->getText(),
$matches
);
foreach ($matches['annotations'] as $possibleTest) {
if ($possibleTest === 'test') {
// Found a test
$test = [
'methodName' => $node->name->name,
];
foreach ($matches['annotations'] as $possibleDataProvider) {
// See if this test has a data provider attached
if (str_starts_with($possibleDataProvider, 'dataProvider')) {
$test['dataProvider'] = trim(ltrim($possibleDataProvider, 'dataProvider'));
if ($node instanceof Node\Stmt\ClassMethod) {
foreach ($node->getAttrGroups() as $possibleTestAttributeGroup) {
foreach ($possibleTestAttributeGroup->attrs as $possibleTestAttribute) {
// See if that method has the phpunit Test attribute attached.
$name = $possibleTestAttribute->name->toCodeString();
if ($name === '\\PHPUnit\\Framework\\Attributes\\Test') {
$test = [
'methodName' => $node->name->name,
];
foreach ($node->getAttrGroups() as $possibleDataProviderAttributeGroup) {
foreach ($possibleDataProviderAttributeGroup->attrs as $possibleDataProviderAttribute) {
// See if that method has the phpunit DataProvider attribute attached, too.
$name = $possibleDataProviderAttribute->name->toCodeString();
if ($name === '\\PHPUnit\\Framework\\Attributes\\DataProvider') {
$dataProviderMethodName = $possibleDataProviderAttribute->args[0]->value->value;
$test['dataProvider'] = $dataProviderMethodName;
}
}
}
$this->tests[] = $test;
}
$this->tests[] = $test;
}
}
}
}

/**
* Return array of found tests and their data providers
*
* @return array
*/
public function getTests(): array
{
Expand All @@ -249,8 +244,6 @@ public function getTests(): array

/**
* Return Fully qualified class test name
*
* @return string
*/
public function getFqcn(): string
{
Expand Down
2 changes: 1 addition & 1 deletion Build/phpstan/phpstan-baseline.neon
Expand Up @@ -1181,7 +1181,7 @@ parameters:
path: ../../typo3/sysext/extbase/Tests/Functional/Property/TypeConverter/ObjectConverterTest.php

-
message: "#^Property class@anonymous/extbase/Tests/Functional/Property/TypeConverter/ObjectConverterTest\\.php\\:334\\:\\:\\$name is unused\\.$#"
message: "#^Property class@anonymous/extbase/Tests/Functional/Property/TypeConverter/ObjectConverterTest\\.php\\:313\\:\\:\\$name is unused\\.$#"
count: 1
path: ../../typo3/sysext/extbase/Tests/Functional/Property/TypeConverter/ObjectConverterTest.php

Expand Down
Expand Up @@ -17,6 +17,7 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Authentication;

use PHPUnit\Framework\Attributes\Test;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use TYPO3\CMS\Backend\Authentication\PasswordReset;
Expand Down Expand Up @@ -48,9 +49,7 @@ public function log($level, string|\Stringable $message, array $context = []): v
};
}

/**
* @test
*/
#[Test]
public function isNotEnabledWorks(): void
{
$mailerMock = $this->createStub(MailerInterface::class);
Expand All @@ -62,9 +61,7 @@ public function isNotEnabledWorks(): void
self::assertFalse($subject->isEnabled());
}

/**
* @test
*/
#[Test]
public function isNotEnabledWithNoUsers(): void
{
$mailerMock = $this->createStub(MailerInterface::class);
Expand All @@ -77,9 +74,7 @@ public function isNotEnabledWithNoUsers(): void
self::assertFalse($subject->isEnabled());
}

/**
* @test
*/
#[Test]
public function isEnabledExcludesAdministrators(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/be_users_only_admins.csv');
Expand All @@ -96,9 +91,7 @@ public function isEnabledExcludesAdministrators(): void
self::assertTrue($subject->isEnabled());
}

/**
* @test
*/
#[Test]
public function isEnabledForUserTest(): void
{
$mailerMock = $this->createStub(MailerInterface::class);
Expand Down Expand Up @@ -127,9 +120,7 @@ public function isEnabledForUserTest(): void
self::assertTrue($subject->isEnabledForUser(1));
}

/**
* @test
*/
#[Test]
public function noEmailIsFound(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv');
Expand All @@ -147,9 +138,7 @@ public function noEmailIsFound(): void
$subject->initiateReset($request, $context, $emailAddress);
}

/**
* @test
*/
#[Test]
public function ambiguousEmailIsTriggeredForMultipleValidUsers(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv');
Expand All @@ -167,9 +156,7 @@ public function ambiguousEmailIsTriggeredForMultipleValidUsers(): void
self::assertEquals($emailAddress, $this->logger->records[0]['context']['email']);
}

/**
* @test
*/
#[Test]
public function passwordResetEmailIsTriggeredForValidUser(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv');
Expand All @@ -192,9 +179,7 @@ public function passwordResetEmailIsTriggeredForValidUser(): void
self::assertEquals($username, $this->logger->records[0]['context']['username']);
}

/**
* @test
*/
#[Test]
public function invalidTokenCannotResetPassword(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv');
Expand Down
Expand Up @@ -17,6 +17,8 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Backend\Shortcut;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\Router;
Expand Down Expand Up @@ -53,10 +55,8 @@ protected function setUp(): void
);
}

/**
* @dataProvider shortcutExistsTestDataProvider
* @test
*/
#[DataProvider('shortcutExistsTestDataProvider')]
#[Test]
public function shortcutExistsTest(string $routeIdentifier, array $arguments, int $userid, bool $exists): void
{
$GLOBALS['BE_USER']->user['uid'] = $userid;
Expand Down Expand Up @@ -91,9 +91,7 @@ public static function shortcutExistsTestDataProvider(): \Generator
];
}

/**
* @test
*/
#[Test]
public function addShortcutTest(): void
{
foreach ($this->getShortcutsToAdd() as $shortcut) {
Expand Down Expand Up @@ -130,9 +128,8 @@ public function getShortcutsToAdd(): array

/**
* This effectively also tests ShortcutRepository::initShortcuts()
*
* @test
*/
#[Test]
public function getShortcutsByGroupTest(): void
{
$expected = [
Expand Down
Expand Up @@ -17,6 +17,8 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Clipboard;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Backend\Clipboard\Clipboard;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
Expand Down Expand Up @@ -142,10 +144,8 @@ public static function localizationsAreResolvedDataProvider(): array
];
}

/**
* @dataProvider localizationsAreResolvedDataProvider
* @test
*/
#[DataProvider('localizationsAreResolvedDataProvider')]
#[Test]
public function localizationsAreResolved(
int $pageId,
int $workspaceId,
Expand Down
Expand Up @@ -17,6 +17,7 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Controller;

use PHPUnit\Framework\Attributes\Test;
use Symfony\Component\DependencyInjection\Container;
use TYPO3\CMS\Backend\Controller\BackendController;
use TYPO3\CMS\Backend\Controller\Event\AfterBackendPageRenderEvent;
Expand Down Expand Up @@ -44,9 +45,7 @@ public function setUp(): void
$GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser);
}

/**
* @test
*/
#[Test]
public function backendPageRenderEventIsTriggered(): void
{
/** @var Container $container */
Expand Down Expand Up @@ -78,9 +77,7 @@ static function (AfterBackendPageRenderEvent $event) use (&$state) {
self::assertInstanceOf(AfterBackendPageRenderEvent::class, $state['after-backend-page-render-listener']);
}

/**
* @test
*/
#[Test]
public function flashMessageIsDispatchedForForcedRedirect(): void
{
// Set workspace to disable the site configuration module
Expand Down
Expand Up @@ -17,6 +17,7 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Controller;

use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Backend\Controller\EditDocumentController;
use TYPO3\CMS\Backend\Routing\Route;
use TYPO3\CMS\Backend\Utility\BackendUtility;
Expand Down Expand Up @@ -50,9 +51,7 @@ protected function setUp(): void
$this->normalizedParams = new NormalizedParams([], [], '', '');
}

/**
* @test
*/
#[Test]
public function processedDataTakesOverDefaultValues(): void
{
$request = (new ServerRequest('https://www.example.com/', 'POST'))
Expand Down Expand Up @@ -82,9 +81,7 @@ public function processedDataTakesOverDefaultValues(): void
self::assertEquals(302, $response->getStatusCode());
}

/**
* @test
*/
#[Test]
public function processedDataDoesNotOverridePostWithDefaultValues(): void
{
$request = (new ServerRequest('https://www.example.com/', 'POST'))
Expand Down
Expand Up @@ -17,6 +17,7 @@

namespace TYPO3\CMS\Backend\Tests\Functional\Controller;

use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Backend\Controller\FormInlineAjaxController;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Routing\Route;
Expand Down Expand Up @@ -69,9 +70,7 @@ protected function setUp(): void
$this->subject = new FormInlineAjaxController(new FormDataCompiler());
}

/**
* @test
*/
#[Test]
public function createActionWithNewParentReturnsResponseForInlineChildData(): void
{
$parsedBody = [
Expand All @@ -92,9 +91,7 @@ public function createActionWithNewParentReturnsResponseForInlineChildData(): vo
self::assertNotEmpty($jsonArray['data']);
}

/**
* @test
*/
#[Test]
public function createActionWithExistingParentReturnsResponseForInlineChildData(): void
{
$parsedBody = [
Expand All @@ -115,9 +112,7 @@ public function createActionWithExistingParentReturnsResponseForInlineChildData(
self::assertNotEmpty($jsonArray['data']);
}

/**
* @test
*/
#[Test]
public function createActionWithExistingLocalizedParentReturnsResponseWithLocalizedChildData(): void
{
$parsedBody = [
Expand All @@ -138,9 +133,7 @@ public function createActionWithExistingLocalizedParentReturnsResponseWithLocali
self::assertMatchesRegularExpression('/<option value="1"[^>]* selected="selected">Dansk<\/option>/', $jsonArray['data']);
}

/**
* @test
*/
#[Test]
public function createActionWithExistingLocalizedParentAndNotLocalizableChildReturnsResponseWithChildData(): void
{
unset($GLOBALS['TCA']['tx_testirrecsv_offer']['ctrl']['languageField']);
Expand Down

0 comments on commit c51afbf

Please sign in to comment.