Problem/Motivation

In PHPUnit 10, data provider methods that are not declared static yield deprecation errors in PHPUnit runs like

There were 2 PHPUnit deprecations:

1) Drupal\Tests\Core\Entity\EntityFieldManagerTest::testGetBaseFieldDefinitionsTranslatableEntityTypeDefaultLangcode
Data Provider method Drupal\Tests\Core\Entity\EntityFieldManagerTest::providerTestGetBaseFieldDefinitionsTranslatableEntityTypeDefaultLangcode() is not static

/var/www/html/core/tests/Drupal/Tests/Core/Entity/EntityFieldManagerTest.php:345

2) Drupal\Tests\Core\Entity\EntityFieldManagerTest::testGetBaseFieldDefinitionsTranslatableEntityTypeLangcode
Data Provider method Drupal\Tests\Core\Entity\EntityFieldManagerTest::providerTestGetBaseFieldDefinitionsTranslatableEntityTypeLangcode() is not static

/var/www/html/core/tests/Drupal/Tests/Core/Entity/EntityFieldManagerTest.php:382

Proposed resolution

Declare all data provider methods as static.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3353210

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Title: [PHPUnit 10] @dataProvider methods must be declared static » [PHPUnit 10] @dataProvider methods must be declared static and public
spokje’s picture

Hmm, I get 1124 (hopefully) relevant results for @dataProvider.

How to scope such a thing?

Are we OK with one big patch/MR that should pass with no deprecation errors as mentioned in the IS, using a tweaked composer so it will use PHPUnit 10.x? It is a straightforward replacement IMHO.

mondrake’s picture

I think we should change without composer tweak. It should work now on PHPUnit 9.5 with public static; it will be just ready for the future.

Changes this big, if the scope is only the correction of visibility, I saw being done in beta release windows. Flagging for release manager input.

spokje’s picture

I think we should change without composer tweak. It should work now on PHPUnit 9.5 with public static; it will be just ready for the future.

Ah, I seemed to make myself not clear enough (per usual...)

I meant it should work now/on PHPUnit 9.5, but using PHPUnit 10.x in a "should_not_fail"-patch just to confirm we've got them all.

Anyway: Indeed beta-release window springs to mind :)

mondrake’s picture

#6 no worries, it can also be said I'm dumb (per usual).

We can do that via the MR in #3217904: [meta] Support PHPUnit 10 in Drupal 11.

Or,

  1. tweaking DrupalListener to check the dataprovider methods' visibility and scope, like it's done today for the visibility of the $module property (should be easy, but not really in line with the intent to remove those checks), or
  2. securing the visibility/scope check via PHPStan or PHPStan-phpunit (probably the best approach, but needs work upstream)

mondrake’s picture

MR!3804 implements the DrupalListener check.

prabuela’s picture

Assigned: Unassigned » prabuela
prabuela’s picture

StatusFileSize
new2.33 KB
mondrake’s picture

Title: [PHPUnit 10] @dataProvider methods must be declared static and public » [PP-1][PHPUnit 10] @dataProvider methods must be declared static and public
Status: Active » Postponed
Related issues: +#3351089: Fix PHPStan L1 errors "@dataProvider Foo related method not found."
mondrake’s picture

Title: [PP-1][PHPUnit 10] @dataProvider methods must be declared static and public » [PHPUnit 10] @dataProvider methods must be declared static and public
Status: Postponed » Active

blocker got committed.

longwave’s picture

Re #4/#5 if this can be done with a bulk find-and-replace or similar script (so it can be easily rerolled) then it can be committed in one go in a beta window - bonus points for providing the script so others can reroll it on demand.

If some data providers require separate refactoring (e.g. because they call non-static methods) then it might be best to split those out to a separate issue so they can be handled first.

mondrake’s picture

#14 a possible starting point for a script is #3186661-2: Remove usage of drupalPostForm

mondrake’s picture

StatusFileSize
new529.4 KB
new1.24 KB

A script, not very sophisticated but it does what it should I think, and a patch with a diff after the script is executed.

Instructions: drop the script in the root, rename it to convert.php, execute it, and git diff the repo to produce the patch.

mondrake’s picture

StatusFileSize
new599.55 KB

Some manual tweaks

mondrake’s picture

StatusFileSize
new600.01 KB

With this patch, PHPStan should pinpoint only all the dataproviders that are making non-static calls.

prabuela’s picture

Assigned: prabuela » Unassigned
spokje’s picture

To fix all this Undefined variable: $this because we're now in static-land, we _could_ do something like:

Pre:

  public function nonHtmlResponseProvider() {
    return [
[SNIP] 
      'A dummy that implements AttachmentsInterface' => [get_class($this->prophesize(AttachmentsInterface::class)->reveal())],
    ];
  }

Post:

  public function nonHtmlResponseProvider() {
    return [
[SNIP] 
      'A dummy that implements AttachmentsInterface' => [get_class((new BigPipeResponseAttachmentsProcessorTest())->prophesize(AttachmentsInterface::class)->reveal())],
    ];
  }

which does pass PHPStan checks.

Would that be the way to go here?

mondrake’s picture

Status: Active » Postponed

I think we need to open separate issues for each case and discover the golden pot at then end of each rainbow, there's no one-fits-all approach here I'm afraid. I am working on a first one for $this->randomMachineName(), #3353658: [PHPUnit 10] Provide a static alternative to randomMachineName() and implement in data providers.

Postponing here till we have no $this...

spokje’s picture

All clear, thanks @mondrake.
(Also booh! to Yet Again no pot-o-gold at the end of the rainbow)

mondrake’s picture

StatusFileSize
new1.23 KB

Re the specific case in #20, I think the overarching goal in PHPUnit by making the dataprovider static is to separate the concerns of test data provision from test data consumption, to avoid the risk that object-level common properties pollute either end of the testing cycle.

'Static' makes very clear that the object instance is not getting into the dataprovider.

If that's the case, then we should refrain from getting the test object back into the way.

For curiosity, let's see this that tries to get the prophecy directly from the factory.

mondrake’s picture

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mondrake’s picture

mondrake’s picture

mondrake’s picture

StatusFileSize
new2.47 KB

Updated script to skip conversion of dataproviders that access instance properties ( $this->xxxx ). This MR will touch over 600 files.

mondrake’s picture

StatusFileSize
new4.03 KB

Wow this is tough. Quite close but not there yet.

mondrake’s picture

Status: Postponed » Needs review

The script converts all the dataproviders that do not use calls to instance properties, that will have to be converted separately. The only exception is ConfigEntityValidationTestBase::providerInvalidMachineNameCharacters() that needs to be manually reverted to non-static, because if the method base class does not make calls to $this, the extending classes do.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

So didn't look at all 674 files but skimmed a very good chunk of them and they all appear to be doing what the ticket/script describes and adding static to provider functions in tests.

Did verify only test files appear to be touched, tests are green.

andypost’s picture

Is there a way to prevent commiting new code with non-static methods?

mondrake’s picture

#32 see #7.

There is a PHPStan rule checking for that, but only if PHPUnit 10 is already installed which is a bit pointless IMHO, https://github.com/phpstan/phpstan-phpunit/issues/178.

Using listeners I'm not very keen on since we're going to gut them.

Develop a custom PHPStan rule seems a bit outworldly since one exists already.

So IMHO the best thing to do now is to get these 1k in here, then watch and rebase #3417066: Upgrade PHPUnit to 10, drop Symfony PHPUnit-bridge dependency where the deprecation of non-static dataprovider is reported/baselined. That will tell us if we readd some through the cracks before we bump PHPUnit to 10. Later, it will be PHPUnit 10 itself to tell us.

andypost’s picture

Thank you for elaboration! Sounds like solid plan, maybe rector's rule to have predictable conversion would be great addition)

mondrake’s picture

Rector-phpunit already has some rules for this, https://github.com/rectorphp/rector-phpunit/blob/main/docs/rector_rules_... and https://github.com/rectorphp/rector-phpunit/blob/main/docs/rector_rules_...

I just found them once I had already made the script attached, and since it worked I saw no reason to change anything.

But that could be useful for contrib/custom conversions.

longwave’s picture

Status: Reviewed & tested by the community » Needs review

I was about to commit this and was scanning the diff for the last time when I had a thought: should we add array return types to each of these methods while we are here? Some already have return types, but most do not, and as we are changing the method signature already...

If you disagree and think this is out of scope and we should do it separately, please set it back to RTBC.

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

I think it’s out of scope, in the sense that here we’re focusing in the thing needed for PHPUnit 10.

Adding return types is absolutely something to do, but then what about adding void to the test methods themselves, and using PHPStan array shapes docs to describe the array returned by the data providers, etc etc. A different story.

EDIT - BTW, data providers

must return a value that is iterable, either an array or an object that implements the Traversable interface

https://docs.phpunit.de/en/11.0/writing-tests-for-phpunit.html#data-prov...

so we might have data providers that do not return array

  • longwave committed 654b1f20 on 11.x
    Issue #3353210 by mondrake, PrabuEla, Spokje: [PHPUnit 10] @dataProvider...
longwave’s picture

Status: Reviewed & tested by the community » Fixed

OK, the fact that data providers do not always return array convinced me that this is not the right place to be doing this, and we should handle it all in one go later.

Usually we would also handle this sort of bulk change in the beta phase, but as discussed in Slack it is better to land this now so we can clear the noise and figure out how to solve the issues with the remaining data providers. We should also commit this now before 10.3.x and 11.x diverge, this might become harder once we have other 11.x only changes.

Committed 654b1f2 and pushed to 11.x. Thanks!

wim leers’s picture

Shouldn't this get a change record? Also to inform contrib modules extending core tests how they can do this in a way that does not break BC with 10.2.x and earlier?

Something like:

Core did:

-  public function urlProvider(): array {
+  public static function urlProvider(): array {

To make your contrib module extending this test compatible with >=10.3, make a matching change. But to retain compatibility with <=10.2, also add:

public function urlProvider(): array {
  return self::urlProvider();
}
longwave’s picture

Data providers can already be static, there is no need to add backward compatibility - we did nothing here to core except add static to method declarations, and everything still works on PHPUnit 9.

Added https://www.drupal.org/node/3421393 - we can expand with more examples of how to refactor the more complex cases once we have figured that out ourselves.

mondrake’s picture

Slightly edited the CR, hopefully for good :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.