From b5fa1c2ac9aac29524ef30efd7051eca42dcf68b Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 13 Mar 2023 13:41:45 +0100 Subject: [PATCH] feat: add support for covers attributes (#68) --- README.md | 2 ++ phpstan-baseline.neon | 6 ++++++ phpstan.neon.dist | 3 +++ src/TestCheck/EveryTestHasGroup.php | 21 +++++++++++++++++++ ...veryTestHasSameNamespaceAsCoveredClass.php | 17 ++++++++++----- ...TestHasSameNamespaceAsCoveredClassTest.php | 2 +- .../CoveredClassWithSomeWhitespaceTest.php | 5 ++++- .../tests/CoversAndCoversNothingTest.php | 10 +++++---- .../tests/CoversNothingTest.php | 4 +++- .../tests/IgnoreMultipleCoversTest.php | 8 +++---- .../SameNamespaceAsLinkedCoveredClassTest.php | 4 +++- tests/TestCheck/Fixtures/WithGroup.php | 4 +++- .../TestCheck/Fixtures/WithUnlistedGroup.php | 4 +++- 13 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 phpstan-baseline.neon diff --git a/README.md b/README.md index 08a55a3..fa3e002 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,8 @@ Consider src namespace `Ns` and test namespace `Ns/Tests` then for test `Ns/Test You can use `@covers` or `@coversDefaultClass` annotations to link test with tested class. Use `@coversNothing` annotation to skip this check. +`#[CoversNothing]` and `#[CoversClass]` attributes are supported. + Don't forget to enable `"forceCoversAnnotation="true"` in phpunit config file. ```php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..74d8ad7 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,6 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$className of attribute class PHPUnit\\\\Framework\\\\Attributes\\\\CoversClass constructor expects class\\-string, string given\\.$#" + count: 2 + path: tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/IgnoreMultipleCoversTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 482e73d..3b08a78 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,3 +5,6 @@ parameters: - %currentWorkingDirectory%/tests ignoreErrors: - "~Call to static method PHPUnit\\\\Framework\\\\Assert::assertTrue\\(\\) with true will always evaluate to true~" + +includes: + - phpstan-baseline.neon diff --git a/src/TestCheck/EveryTestHasGroup.php b/src/TestCheck/EveryTestHasGroup.php index feddff3..ab4c104 100644 --- a/src/TestCheck/EveryTestHasGroup.php +++ b/src/TestCheck/EveryTestHasGroup.php @@ -5,6 +5,7 @@ namespace Cdn77\TestUtils\TestCheck; use Cdn77\EntityFqnExtractor\ClassExtractor; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -25,6 +26,26 @@ public function run(TestCase $testCaseContext): void { foreach ($this->filePathNames as $filePathName) { $classReflection = new ReflectionClass(ClassExtractor::get($filePathName)); + if ($classReflection->isAbstract()) { + continue; + } + + $groupAttributes = $classReflection->getAttributes(Group::class); + foreach ($groupAttributes as $groupAttribute) { + $testCaseContext::assertContains( + $groupAttribute->getArguments()[0], + $this->allowedGroups, + sprintf( + 'Test "%s" has invalid @group annotation "%s"', + $classReflection->getName(), + $groupAttribute->getArguments()[0], + ), + ); + } + + if ($groupAttributes !== []) { + continue; + } $this->validateDocComment($testCaseContext, $classReflection); } diff --git a/src/TestCheck/EveryTestHasSameNamespaceAsCoveredClass.php b/src/TestCheck/EveryTestHasSameNamespaceAsCoveredClass.php index dc0828e..a7d9d5a 100644 --- a/src/TestCheck/EveryTestHasSameNamespaceAsCoveredClass.php +++ b/src/TestCheck/EveryTestHasSameNamespaceAsCoveredClass.php @@ -5,6 +5,8 @@ namespace Cdn77\TestUtils\TestCheck; use Cdn77\EntityFqnExtractor\ClassExtractor; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -38,22 +40,27 @@ public function run(TestCase $testCaseContext): void foreach ($this->filePathNames as $file) { $classReflection = new ReflectionClass(ClassExtractor::get($file)); + $attributesCoversClass = $classReflection->getAttributes(CoversClass::class); + $attributesCoversNothing = $classReflection->getAttributes(CoversNothing::class); + $docComment = $classReflection->getDocComment(); if ($docComment === false) { $docComment = ''; } - $matchesCovers = preg_match_all(self::PatternCovers, $docComment, $coversMatches) > 0; - $matchesCoversNothing = preg_match(self::PatternCoversNothing, $docComment) === 1; + $hasCovers = preg_match_all(self::PatternCovers, $docComment, $coversMatches) > 0 + || $attributesCoversClass !== []; + $hasCoversNothing = preg_match(self::PatternCoversNothing, $docComment) === 1 + || $attributesCoversNothing !== []; - if ($matchesCovers && $matchesCoversNothing) { + if ($hasCovers && $hasCoversNothing) { $testCaseContext::fail(sprintf( - 'Test file "%s" contains both @covers and @coversNothing annotations.', + 'Specifying CoversClass and CoversNothing attributes at the same time makes no sense (in "%s").', $file, )); } - if ($matchesCoversNothing || $matchesCovers) { + if ($hasCoversNothing || $hasCovers) { continue; } diff --git a/tests/TestCheck/EveryTestHasSameNamespaceAsCoveredClassTest.php b/tests/TestCheck/EveryTestHasSameNamespaceAsCoveredClassTest.php index f80a716..4241e9d 100644 --- a/tests/TestCheck/EveryTestHasSameNamespaceAsCoveredClassTest.php +++ b/tests/TestCheck/EveryTestHasSameNamespaceAsCoveredClassTest.php @@ -56,7 +56,7 @@ public static function providerFail(): Generator { yield [ 'CoversAndCoversNothingTest.php', - 'contains both @covers and @coversNothing annotations', + 'Specifying CoversClass and CoversNothing attributes at the same time', ]; yield [ diff --git a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoveredClassWithSomeWhitespaceTest.php b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoveredClassWithSomeWhitespaceTest.php index 5fb670b..8616f87 100644 --- a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoveredClassWithSomeWhitespaceTest.php +++ b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoveredClassWithSomeWhitespaceTest.php @@ -4,7 +4,10 @@ namespace Cdn77\TestUtils\Tests\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass; -/** @covers \Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass\SameNamespace */ +use Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass\SameNamespace; +use PHPUnit\Framework\Attributes\CoversClass; + +#[CoversClass(SameNamespace::class)] final class CoveredClassWithSomeWhitespaceTest { } diff --git a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversAndCoversNothingTest.php b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversAndCoversNothingTest.php index 86765d0..4276980 100644 --- a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversAndCoversNothingTest.php +++ b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversAndCoversNothingTest.php @@ -4,10 +4,12 @@ namespace Cdn77\TestUtils\Tests\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass; -/** - * @coversNothing - * @covers \stdClass - */ +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversNothing; +use stdClass; + +#[CoversClass(stdClass::class)] +#[CoversNothing()] final class CoversAndCoversNothingTest { } diff --git a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversNothingTest.php b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversNothingTest.php index 03c2183..c0ee635 100644 --- a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversNothingTest.php +++ b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/CoversNothingTest.php @@ -4,7 +4,9 @@ namespace Cdn77\TestUtils\Tests\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass; -/** @coversNothing */ +use PHPUnit\Framework\Attributes\CoversNothing; + +#[CoversNothing] final class CoversNothingTest { } diff --git a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/IgnoreMultipleCoversTest.php b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/IgnoreMultipleCoversTest.php index e0287e6..b8f7bf4 100644 --- a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/IgnoreMultipleCoversTest.php +++ b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/IgnoreMultipleCoversTest.php @@ -4,10 +4,10 @@ namespace Cdn77\TestUtils\Tests\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass; -/** - * @covers Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsTestedClass\A - * @covers Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsTestedClass\B - */ +use PHPUnit\Framework\Attributes\CoversClass; + +#[CoversClass('\Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsTestedClass\A')] +#[CoversClass('\Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsTestedClass\B')] final class IgnoreMultipleCoversTest { } diff --git a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/SameNamespaceAsLinkedCoveredClassTest.php b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/SameNamespaceAsLinkedCoveredClassTest.php index 8796bd2..f117c46 100644 --- a/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/SameNamespaceAsLinkedCoveredClassTest.php +++ b/tests/TestCheck/Fixtures/EveryTestHasSameNamespaceAsCoveredClass/tests/SameNamespaceAsLinkedCoveredClassTest.php @@ -4,7 +4,9 @@ namespace Cdn77\TestUtils\Tests\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass; -/** @covers Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass\SameNamespace */ +use PHPUnit\Framework\Attributes\CoversClass; + +#[CoversClass('\Cdn77\TestUtils\Tests\TestCheck\Fixtures\EveryTestHasSameNamespaceAsCoveredClass\SameNamespace')] final class SameNamespaceAsLinkedCoveredClassTest { } diff --git a/tests/TestCheck/Fixtures/WithGroup.php b/tests/TestCheck/Fixtures/WithGroup.php index 91252f2..f7f0592 100644 --- a/tests/TestCheck/Fixtures/WithGroup.php +++ b/tests/TestCheck/Fixtures/WithGroup.php @@ -4,7 +4,9 @@ namespace Cdn77\TestUtils\Tests\TestCheck\Fixtures; -/** @group unit */ +use PHPUnit\Framework\Attributes\Group; + +#[Group('unit')] final class WithGroup { } diff --git a/tests/TestCheck/Fixtures/WithUnlistedGroup.php b/tests/TestCheck/Fixtures/WithUnlistedGroup.php index 5d37997..07e5f86 100644 --- a/tests/TestCheck/Fixtures/WithUnlistedGroup.php +++ b/tests/TestCheck/Fixtures/WithUnlistedGroup.php @@ -4,7 +4,9 @@ namespace Cdn77\TestUtils\Tests\TestCheck\Fixtures; -/** @group Eheu, raptus advena! */ +use PHPUnit\Framework\Attributes\Group; + +#[Group('Eheu, raptus advena!')] final class WithUnlistedGroup { }