Skip to content

Detect the use of Attributes for PHPUnit Sniffs #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

andrewnicols
Copy link
Contributor

@andrewnicols andrewnicols commented Aug 6, 2025

Fixes #205

Blocked by #206 and #207

Copy link

codecov bot commented Aug 6, 2025

Codecov Report

❌ Patch coverage is 99.50980% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 98.34%. Comparing base (378a85f) to head (68254b8).

Files with missing lines Patch % Lines
moodle/Util/NamespaceScopeUtil.php 96.77% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #207      +/-   ##
============================================
+ Coverage     98.28%   98.34%   +0.06%     
- Complexity      959     1000      +41     
============================================
  Files            40       42       +2     
  Lines          2971     3140     +169     
============================================
+ Hits           2920     3088     +168     
- Misses           51       52       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@andrewnicols andrewnicols marked this pull request as draft August 6, 2025 14:47
@andrewnicols andrewnicols force-pushed the testcaseAttributes branch 2 times, most recently from ea1cd85 to b45d6fc Compare August 6, 2025 15:23
@andrewnicols andrewnicols marked this pull request as ready for review August 6, 2025 15:25
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements support for detecting PHP 8.0+ attributes in PHPUnit test case sniffs, specifically adding support for #[DataProvider] and other PHPUnit attributes alongside existing phpdoc-based annotations.

  • Adds a new NamespaceScopeUtil helper class to properly qualify class names considering imports and namespaces
  • Extends PHPUnit sniffs to support both traditional phpdoc comments and modern PHP attributes
  • Creates a shared AbstractTestCaseSniff base class to consolidate common functionality

Reviewed Changes

Copilot reviewed 14 out of 39 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
moodle/Util/NamespaceScopeUtil.php New utility class for qualifying class names within namespace scope
moodle/Util/Attributes.php Enhanced with qualified name support and new helper methods
moodle/Tests/Util/NamespaceScopeUtilTest.php Test coverage for the new namespace utility
moodle/Tests/Util/AttributesTest.php Updated tests for enhanced attribute functionality
moodle/Tests/Sniffs/PHPUnit/fixtures/Provider/attributes_test.php Test fixture for attribute-based data providers
moodle/Tests/Sniffs/PHPUnit/fixtures/Covers/testcasecovers_method_attribute.php Test fixture for method-level coverage attributes
moodle/Tests/Sniffs/PHPUnit/fixtures/Covers/testcasecovers_attribute.php Test fixture for class-level coverage attributes
moodle/Tests/Sniffs/PHPUnit/fixtures/Covers/testcasecovers_abstract.php Test fixture for abstract test classes
moodle/Tests/Sniffs/PHPUnit/TestCaseProviderTest.php Updated test with attribute support cases
moodle/Tests/Sniffs/PHPUnit/TestCaseCoversTest.php Updated test with attribute support cases
moodle/Sniffs/PHPUnit/TestCaseProviderSniff.php Enhanced to detect and validate DataProvider attributes
moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php Enhanced to detect and validate coverage attributes
moodle/Sniffs/PHPUnit/AbstractTestCaseSniff.php New base class for PHPUnit sniffs with shared functionality
CHANGELOG.md Documentation of the changes

use Example\Thing\AnotherExample;
',
T_CLASS,
['Example' => \Example\Thing\Example::class],
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \Example\Thing\Example::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: ['Example' => 'Example\Thing\Example'].

Suggested change
['Example' => \Example\Thing\Example::class],
['Example' => 'Example\Thing\Example'],

Copilot uses AI. Check for mistakes.

use Example\Thing\AnotherExample;
',
T_CLASS,
['OtherExample' => \Example\Thing\Example::class],
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \Example\Thing\Example::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: ['OtherExample' => 'Example\Thing\Example'].

Suggested change
['OtherExample' => \Example\Thing\Example::class],
['OtherExample' => 'Example\Thing\Example'],

Copilot uses AI. Check for mistakes.

Comment on lines +198 to +202
'Another' => \My\Full\Classname::class,
'NSname' => \My\Full\NSname::class,
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \My\Full\Classname::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'Another' => 'My\Full\Classname'.

Suggested change
'Another' => \My\Full\Classname::class,
'NSname' => \My\Full\NSname::class,
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
'Another' => 'My\Full\Classname',
'NSname' => 'My\Full\NSname',
'ClassA' => 'some\name\space\ClassA',
'ClassB' => 'some\name\space\ClassB',
'C' => 'some\name\space\ClassC',

Copilot uses AI. Check for mistakes.

Comment on lines +198 to +202
'Another' => \My\Full\Classname::class,
'NSname' => \My\Full\NSname::class,
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \My\Full\NSname::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'NSname' => 'My\Full\NSname'.

Suggested change
'Another' => \My\Full\Classname::class,
'NSname' => \My\Full\NSname::class,
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
'Another' => 'My\Full\Classname',
'NSname' => 'My\Full\NSname',
'ClassA' => 'some\name\space\ClassA',
'ClassB' => 'some\name\space\ClassB',
'C' => 'some\name\space\ClassC',

Copilot uses AI. Check for mistakes.

Comment on lines +200 to +202
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \some\name\space\ClassA::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'ClassA' => 'some\name\space\ClassA'.

Suggested change
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
'ClassA' => 'some\name\space\ClassA',
'ClassB' => 'some\name\space\ClassB',
'C' => 'some\name\space\ClassC',

Copilot uses AI. Check for mistakes.

'NSname' => \My\Full\NSname::class,
'ClassA' => \some\name\space\ClassA::class,
'ClassB' => \some\name\space\ClassB::class,
'C' => \some\name\space\ClassC::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \some\name\space\ClassC::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'C' => 'some\name\space\ClassC'.

Suggested change
'C' => \some\name\space\ClassC::class,
'C' => 'some\name\space\ClassC',

Copilot uses AI. Check for mistakes.

class Example {
}',
T_CLASS,
\Example\Attribute::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \Example\Attribute::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'Example\Attribute'.

Suggested change
\Example\Attribute::class,
'Example\Attribute',

Copilot uses AI. Check for mistakes.

class Example {
}',
T_CLASS,
\Example\MyFirstAttribute::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \Example\MyFirstAttribute::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'Example\MyFirstAttribute'.

Suggested change
\Example\MyFirstAttribute::class,
'Example\MyFirstAttribute',

Copilot uses AI. Check for mistakes.

class Example {
}',
T_CLASS,
\Example\MyFirstAttribute::class,
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using \Example\MyFirstAttribute::class which assumes the class exists, but this will cause a fatal error during test execution. Use a string representation instead: 'Example\MyFirstAttribute'.

Suggested change
\Example\MyFirstAttribute::class,
'Example\MyFirstAttribute',

Copilot uses AI. Check for mistakes.

}

// Ignore any classname which does not end in "_test" or "_testcase".
if (substr($className, -5) !== '_test' && substr($className, -10) !== '_testcase') {
Copy link
Preview

Copilot AI Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checking for '_testcase' suffix is incorrect. It checks for -10 characters but '_testcase' is 9 characters long. Should be substr($className, -9) !== '_testcase'.

Suggested change
if (substr($className, -5) !== '_test' && substr($className, -10) !== '_testcase') {
if (substr($className, -5) !== '_test' && substr($className, -9) !== '_testcase') {

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Teach PHPUnit sniffs to understand Attributes as well of Annotations
1 participant