Problem/Motivation

PHPUnit 8 fails somewhat aggressively for its own deprecations, with TestCase::addWarning(). New methods that don't exist for PHPUnit 6 are required for tests to pass on PHPUnit 8. To work around this, Drupal 9.0 overrode TestCase::addWarning() to skip warnings for all new deprecation in PHPUnit 8, in \Drupal\Tests\Traits\PHPUnit8Warnings. This works differently than the suppression of deprecation failures from the Symfony PHPUnit bridge.

As we cleaned up core's use of the deprecated methods, we removed each warning from the list of silenced warnings. This has mostly happened during Drupal 9.0's beta, while 8.7 is still supported and 9.1.x is open for new development..

Because the warnings are being raised by PHPUnit itself and not the PHPUnit bridge, this means marking tests @legacy or using --suppress-deprecationson Drupal CI is not sufficient to avoid a test failure. It also means that tests that need to assert things related to these assertions can't be compatible with 8.7 and 9.0's beta at the same time, because even though we backported foward-compatibility shims all the way into 8.8 during its stable release, we can't and won't backport new APIs to the security-support-only branch. (Furthermore, a couple methods could not be backported to Drupal 8 because they would not be straightforward to implement on PHP 7.0.)

Proposed resolution

Normally, we don't add disruptive changes like new deprecations to a branch after it's in beta. Disruptive changes should only be going into 9.1.x at the moment. Unsilencing a deprecation is a disruptive change.

So, restore the full list of PHPUnit8 warnings to ignore in 9.0.x only. That way, 9.0 compatibility doesn't require dealing with the full gamut of "new" deprecations from PHPUnit 8, and more module can use 8^ || 9^ compatibility rather than ^8.8. || ^9. 9.1.x will still fail if we accidentally reintroduce usage of one of the deprecated methods, and since the backport policy requires things be committed to 9.1.x first, that means we're unlikely to regress 9.0 either. (Both 9.0 and 8.9 will need to be compatible with PHP 8, and therefore PHPUnit 9.)

Remaining tasks

Decide if this is actually the best way to go, or if we do want these fails to force contrib to be forward-compatible with PHPUnit 9 (and therefore PHP 8).

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#81 3142267-2-81.patch15.26 KBalexpott
#81 72-81-interdiff.txt671 bytesalexpott
#72 3142267-72.patch15.26 KBjungle
#72 raw-interdiff-66-72.txt182 bytesjungle
#72 raw-interdiff-53-72.txt416 bytesjungle
#67 64-66-interdiff.txt603 bytesalexpott
#66 3142267-66.patch15.26 KBmohrerao
#64 interdiff_53-64.txt1.9 KBmohrerao
#64 3142267-64.patch14.92 KBmohrerao
#53 3142267-53.patch15.28 KBalexpott
#53 49-53-interdiff.txt746 bytesalexpott
#49 3142267-49.patch15.24 KBalexpott
#49 43-49-interdiff.txt9.35 KBalexpott
#43 3142267-43.patch7.06 KBalexpott
#43 38-43-interdiff.txt1.66 KBalexpott
#38 3142267-38.patch7.61 KBalexpott
#38 37-38-interdiff.txt1.12 KBalexpott
#37 3142267-37.patch8.73 KBalexpott
#37 35-37-interdiff.txt6.23 KBalexpott
#35 interdiff-3142267-35.txt736 bytesxjm
#35 phpunit-3142267-35.patch6.64 KBxjm
#31 phpunit-3142267-30-FAIL.patch3.21 KBxjm
#30 interdiff-3142267-30.txt8.6 KBxjm
#30 phpunit-3142267-30.patch6.64 KBxjm
#22 interdiff-3142267-22.patch494 bytesxjm
#22 phpunit-3142267-22.patch5.82 KBxjm
#19 interdiff-3142267-19.txt687 bytesxjm
#19 phpunit-3142267-19.patch5.85 KBxjm
#18 interdiff-3142267-18.txt2.37 KBxjm
#18 phpunit-3142267-18.patch5.85 KBxjm
#12 interdiff-3142267-12.txt3.63 KBxjm
#12 phpunit-3142267-12.patch6.01 KBxjm
#12 phpunit-3142267-12-FAIL.patch2.14 KBxjm
#9 interdiff-3142267-9.txt2.54 KBxjm
#9 phpunit-3142267-9.patch3.65 KBxjm
#5 interdiff-3142267-5.txt995 bytesxjm
#5 phpunit-3142267-5.patch2.14 KBxjm
#2 phpunit8-warnings.patch2.14 KBxjm
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

xjm created an issue. See original summary.

xjm’s picture

Status: Active » Needs review
FileSize
2.14 KB
xjm’s picture

Issue summary: View changes
jungle’s picture

xjm’s picture

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -23,7 +23,19 @@ trait PHPUnit8Warnings {
-    'expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9.',

Oops, betting this will cause a fail. Removing it wasn't wholly intentional. (I just checked out the original version of the trait.)

xjm’s picture

Issue summary: View changes

The last submitted patch, 2: phpunit8-warnings.patch, failed testing. View results

alexpott’s picture

How about we put then in a new list of warnings and use the SYMFONY_DEPRECATIONS_HELPER flag and ignore them if it is equal to disabled or the test has the legacy group?

That way a project can move forward quickly without having to fix these things. And it'll make it easier to prep 8.9.x for PHPUnit 9.

xjm’s picture

Title: [9.0 only] Restore the full list of PHPUnit8Warnings::$ignoredWarnings in 9.0.x only » [9.0 only?] Restore the full list of PHPUnit8Warnings::$ignoredWarnings
Issue tags: +Needs tests
FileSize
3.65 KB
2.54 KB

Something like this?

We could discuss whether to also commit this to 9.1.x or to raise hard failures there.

longwave’s picture

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -18,25 +18,65 @@
+      if (!('disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER'))) {

Double negative here is a bit hard to read, this can just be === I think?

Otherwise this seems like a good approach.

xjm’s picture

@longwave Ah yep, thanks I actually hemmed and hawed a bit about the negation and the strict comparison, but that was before I moved stuff around from a previous iteration.

xjm’s picture

xjm’s picture

Status: Needs review » Needs work
  1. +++ b/core/tests/Drupal/Tests/SkippedDeprecationTest.php
    @@ -2,6 +2,8 @@
    +use PHPUnit\Framework\Error\Warning;
    +
    

    This use statement is currently unused because...

  2. +++ b/core/tests/Drupal/Tests/SkippedDeprecationTest.php
    @@ -27,4 +29,47 @@ public function testSkippingDeprecationsAgain() {
    +    // @todo This doesn't work.
    +    // $this->addWarning('Warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$corePassingDeprecationsWarnings');
    +    // $this->expectException(Warning::class);
    

    ...I thought I'd figured out how PHPUnit warnings worked, but after reading it again I think this may be a thing where it converts PHP warnings to exceptions, not its own warnings being handled as exceptions. Couldn't work out how to treat the warning as expected, but maybe there isn't a way (because if there were a straightforward way to do that then we probably wouldn't have to have overridden addWarning() in the first place).

If anyone has a notion how to test for case 4, feel free.

xjm’s picture

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -27,6 +27,8 @@ trait PHPUnit8Warnings {
+    // Test assertion.

@@ -51,6 +53,8 @@ trait PHPUnit8Warnings {
+    // Test assertion.

Er, and it's a test warning obviously, not a test assertion.

catch’s picture

The proposed resolution seems good here, although definitely agreed this should only happen in 9.0.x so that they start getting surfaced again in 9.1.x

xjm’s picture

@catch Well, a module could legitimately decide it doesn't care about PHPUnit 9 support (and therefore PHP 8 support), so respecting the behavior of --suppress-deprecations could be useful for all branches. It's only an accident of how PHPUnit has chosen to handle these deprecations that tests are failing.

And it's reasonably possible we'll run into the same thing again with PHPUnit 10 if they continue to use addWarning() to raise a deprecation; even if we make 9.0 play nice, we'll still have to jump through hoops to support the multiple major versions at once again later.

xjm’s picture

Title: [9.0 only?] Restore the full list of PHPUnit8Warnings::$ignoredWarnings » [9.0 only?] Allow the full list of deprecations from PHPUnit8Warning to respect the --suppress-deprecations behavior on DrupalCI
xjm’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
5.85 KB
2.37 KB

Just removing the @todo code from the test so that this can be NR, and also making the inline docs in both methods consistent. I'm not sure testing the last case is possible without further overriding addWarning() with API to test overriding addWarning(), which feels very self-referential.

xjm’s picture

Well, the docs shouldn't be completely consistent.

alexpott’s picture

Re #18 yeah doing that feels wrong. We could also use reflection to inspect the warning private and then reset it which is also wrong. It is testable but we'd need to test in the same way that PHPUnit tests itself for end-to-end testing. To do this we'd need to support .phpt tests. Then we could make the test something like:

  /**
   * Tests not skipping PHPUnit deprecation warnings.
   *
   * @see Drupal\Tests\Traits\PHPUnit8Warnings
   */
  public function testNotSkippingPhpUnit8Warnings() {
    if (!getenv('SkippedDeprecationTestWarning')) {
      $this->markTestSkipped('This test is only triggered by core/tests/Drupal/Tests/PhpUnitWarnings.phpt as it tests PHPUnit itself.');
    }
    $original_setting = getenv('SYMFONY_DEPRECATIONS_HELPER');

    // Ensure the deprecation helper is enabled regardless of the configured
    // setting.
    putenv('SYMFONY_DEPRECATIONS_HELPER=');

    // The warning that core fails should still be silenced.
    $this->addWarning('Warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$coreFailingWarnings');
    // The warning that core does not fail should be flagged.
    // @todo This doesn't work.
    $this->addWarning('Warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$corePassingDeprecationsWarnings');

    putenv("SYMFONY_DEPRECATIONS_HELPER=$original_setting");
  }

And then add a file called core/tests/Drupal/Tests/SkippedDeprecationTest.php which contains something like:

--TEST--
phpunit warnings test
--FILE--
<?php
putenv('SkippedDeprecationTestWarning=1');
$_SERVER['argv'][1] = '--no-configuration';
$_SERVER['argv'][3] = __DIR__ . '/SkippedDeprecationTest.php';
$_SERVER['argv'][4] = '--filter';
$_SERVER['argv'][5] = 'testNotSkippingPhpUnit8Warnings';

require __DIR__ . '/../../bootstrap.php';
PHPUnit\TextUI\Command::main();
--EXPECTF--
PHPUnit %s by Sebastian Bergmann and contributors.

W                                                                   1 / 1 (100%)

Time: %s ms, Memory: %s MB

There was 1 warning:

1) Drupal\Tests\SkippedDeprecationTest::testNotSkippingPhpUnit8Warnings
Warning for \Drupal\Tests\Traits\PHPUnit8Warnings::$corePassingDeprecationsWarnings

%s
%s
%s
%s
%s

WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.

I'll open a follow-up to discuss adding support for this type of test. PHPunit will run .phpt tests but our \Drupal\Core\Test\TestDiscovery very much only works with classes and makes a series of assumptions.

alexpott’s picture

This looks RTBC to me apart from a minor nit - and something that might need a followup issue with the coder module.

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -18,25 +18,69 @@
+   *
+   * @return void

Unfortunately this fails our PHPCS check with

FILE: ...upal8alt.dev/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 66 | ERROR | If there is no return value for a function, there must
    |       | not be a @return tag.
    |       | (Drupal.Commenting.FunctionComment.VoidReturn)
----------------------------------------------------------------------
+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -18,25 +18,69 @@
+      // Return early only if deprecation errors are specifically suppressed.
+      if (getenv('SYMFONY_DEPRECATIONS_HELPER') === 'disabled') {

I considered whether we should add something like

      $method = $this->getName(FALSE);
      if (strpos($method, 'testLegacy') === 0
        || strpos($method, 'provideLegacy') === 0
        || strpos($method, 'getLegacy') === 0
        || strpos(get_class($this), '\Legacy')
        || in_array('legacy', Test::getGroups(get_class($this), $method), TRUE)) {
        // This is a legacy test...
      }

To detect whether the warning is occurring in a legacy test but I think only using the SYMFONY_DEPRECATIONS_HELPER is the way to go here. As I'm not sure what we should do in this case. Like should we be converting the warning into an @trigger_error? Feels like extra complexity. And this change has the desired effect. Contrib and custom can test without this tripping them up and the y can also test and see the warning - all with the convenience of using the same flag that we already use to surface deprecations in the test system.

Actually maybe that suggests a neater way of approaching the whole issue. We could convert the entire original list into deprecation errors (i.e. @trigger_error('blah', E_USER_DEPRECATED)) and then in \Drupal\Tests\Listeners\DeprecationListenerTrait::getSkippedDeprecations() skip the one core still triggers. That's still allow contrib/custom to ignore these with SYMFONY_DEPRECATIONS_HELPER but they'd be handled like all the other deprecations we have. It would also mean less code paths and simpler testing.

xjm’s picture

Title: [9.0 only?] Allow the full list of deprecations from PHPUnit8Warning to respect the --suppress-deprecations behavior on DrupalCI » Allow the full list of deprecations from PHPUnit8Warning to respect the --suppress-deprecations behavior on DrupalCI
FileSize
5.82 KB
494 bytes

@catch and I agreed that this can go into 9.1.x as well with the current approach being configurable anyway.

I'm not totally sure we should do our own @trigger_error() since these are PHPUnit's errors. It's one thing to let them not fail tests, but it seems like if the tests do fail they should still do it with the PHPUnit warning?

alexpott’s picture

Well the advantage of using @trigger_error is that we have a single logic path so we don't have to add

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -18,25 +18,67 @@
+    // Test assertion.
+    'Warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$coreFailingWarnings',

For testing purposes because the list will never change as we won't need to change it. Also it means that all code paths are testable with our current infrastructure. I think less logic and more testable is a good reason to do something.

xjm’s picture

The PHPUnit warnings fail like:

Testing Drupal\Tests\typed_data\Kernel\DataFilterTest
.W....                                                              6 / 6 (100%)

Time: 5.52 seconds, Memory: 4.00 MB

There was 1 warning:

1) Drupal\Tests\typed_data\Kernel\DataFilterTest::testDefaultFilter
Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.

/var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php:691

 

Whereas deprecations fail like:

Testing Drupal\KernelTests\Core\Entity\EntityQueryTest
...............                                                   15 / 15 (100%)

Time: 35.06 seconds, Memory: 4.00 MB

OK (15 tests, 252 assertions)

Remaining self deprecation notices (35)

  35x: AssertLegacyTrait::assertIdentical() is deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertSame() instead. See https://www.drupal.org/node/3129738
    18x in EntityQueryTest::testEntityQuery from Drupal\KernelTests\Core\Entity
    10x in EntityQueryTest::testDelta from Drupal\KernelTests\Core\Entity
    5x in EntityQueryTest::testSort from Drupal\KernelTests\Core\Entity
    1x in EntityQueryTest::testTableSort from Drupal\KernelTests\Core\Entity
    1x in EntityQueryTest::testNestedConditionGroups from Drupal\KernelTests\Core\Entity
alexpott’s picture

Another advantage of converting to @trigger_error() is support for ignoring the deprecations where you are doing deprecations in legacy tests.

mondrake’s picture

+1 in the idea to convert the warning into a @trigger_error.

Also I think that we should not override addWarning in the trait, since it’s a PHPUnit internal: https://github.com/sebastianbergmann/phpunit/blob/4dc5717d74120d27d7dc37...

IMHO the correct way should be somehow to intercept the added warning in a test listener ... but I have not checked if possible :)

alexpott’s picture

@mondrake I don't think

IMHO the correct way should be somehow to intercept the added warning in a test listener

is possible. The way warnings work in PHPUnit occurs outside anything that could catch the exception it results in. See \PHPUnit\Framework\TestCase::runBare() and once $listener->addWarning() is being called in \PHPUnit\Framework\TestResult::addWarning() the test is already marked as failing.

mondrake’s picture

@alexpott thanks for feedback. It's a pity, though. Need to keep a watch on what PHPUnit 10 will bring in terms of replacing the listeners.

xjm’s picture

I care more about getting this in to stop the bleeding than I do about the exact fails, but I still think it is a wrong choice architecturally to "demote" PHPUnit's own warnings from an actual warning to an "OK" message, especially since that will also change the results on the command line when the tests are run locally.

I'll provide a patch that implements the other way and then get a third committer opinion.

xjm’s picture

Here's a patch that nerfs the warnings into E_USER_DEPRECATED. Note that #23 is not correct, we still have to add the test messages. That's also what getSkippedDeprecations() already does. In fact, we now have to add one of the entries in two places plus the test, rather than only one place plus the test.

It is one less code path in the method itself, and it's nicer to not have to fiddle environment variables in a test, but I still think actually converting PHPUnit's version of deprecations into our own is not the right choice.

xjm’s picture

Forgot the test-only patch.

Status: Needs review » Needs work

The last submitted patch, 31: phpunit-3142267-30-FAIL.patch, failed testing. View results

xjm’s picture

Status: Needs work » Needs review

Expected fail.

longwave’s picture

One thing I don't get is why these switches exist:

$ vendor/bin/phpunit --help|grep warning
  --stop-on-warning           Stop execution upon first warning
  --fail-on-warning           Treat tests with warnings as failures

It seems like fail-on-warning is happening by default, but I can't see where, or how we can turn it off again.

+++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php
@@ -21,6 +21,18 @@ public function testDeprecatedClass() {
+   * @see Drupal\Tests\Traits\PHPUnit8Warnings

@see should be fully qualified, ie. with a leading backslash.

xjm’s picture

Indeed it should.

jungle’s picture

Just FYI, not sure if it's relevant here:

\Drupal\Tests\Listeners\DrupalListener implements a deprecated interface TestListener

And a link: Make TestHook interfaces as easy to use as TestListener #3390


# \PHPUnit\Framework\TestListener

/**
 * @deprecated Use the `TestHook` interfaces instead
 */
interface TestListener
alexpott’s picture

Another advantage of changing to deprecations is that we can actually use the deprecated in a method in a test rather than adding

+++ b/core/tests/Drupal/Tests/Traits/PHPUnit8Warnings.php
@@ -18,25 +18,52 @@
+    // Warnings for testing.
+    'Skipped warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$deprecationWarnings',
+    'Un-skipped warning for \\Drupal\\Tests\\Traits\\PHPUnit8Warnings::$deprecationWarnings',

We don't need to test skipping deprecations as that functionality is not part of this change. And if we actually call the deprecated methods then we don't need the other string.

The patch attached contains a test of the each deprecated method and shows that we're triggering the expected deprecations and more importantly that PHPUnit is not triggering other warnings. This means that if PHPUnit 8 adds any new deprecations in the meantime that effect these methods then this test will catch it. And on the issue that fixes the expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9. deprecation we can add another test to PHPUnit8WarningsTest.

It also means that if there other deprecated PHPUnit 8 methods that contrib is leveraging then we can add them to the list and test the deprecation in core too.

If we make Drupal compatible with PHPUnit 9 then we'll need to skip this test there but that's okay too.q

alexpott’s picture

Oops forgot to revert the changes to PhpUnitBridgeTest.

The last submitted patch, 37: 3142267-37.patch, failed testing. View results

mondrake’s picture

+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -142,6 +142,9 @@ public static function getSkippedDeprecations() {
       '\Drupal\Tests\SkippedDeprecationTest deprecation',
+      // The following are PHPUnit 8 deprecations that need to be resolved prior
+      // to PHPUnit 9 compatibility.
+      'expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9.',
       // The following Symfony deprecations are introduced in the Symfony 4

Can't we find a way not to have this in two places, but just in one, and have both DeprecationListenerTrait and PHPUnit8Warnings implementations access it? Reduce c/p carpal tunnel syndrome symptoms.

alexpott’s picture

@mondrake I don't think so. The point is that we need to do the work to remove it from the skipped deprecations and then it remains in the PhpUnit8Warnings trait. The skipped deprecation list is a list of technical debt that we have. We can easily remove it from the list so we have it in one place - we do the work in core to stop calling the method. Created #3143235: Convert ::expectExceptionMessageRegExp to ::expectExceptionMessageMatches to handle this.

alexpott’s picture

So if we land #3143235-4: Convert ::expectExceptionMessageRegExp to ::expectExceptionMessageMatches in 9.0.x and 9.1.x then we don't have to add this to the list of skipped deprecations and can add a test for expectExceptionMessageRegExp triggering a deprecation.

alexpott’s picture

xjm’s picture

This is removing a lot of test coverage; why are we doing that?

xjm’s picture

It is part of the change to validate that the PHPUnit warnings behave the same way both skipped and un-skipped as the deprecation warnings. We can't just assume that's the case because that's what the API does -- it needs to actually be tested as well.

mondrake’s picture

In #3127141: Use PHPUnit 9 for PHP 7.4+, while keeping support for PHPUnit 8.4 in PHP 7.3 we are suggesting to replace PHPUnit8Warnings with a more generic class name - since it's pretty clear this is there to stay in later PHPUnit versions. PHPUnit 9 already has warnings triggered for methods that will be remoed in PHPUnit 10.

I wonder whether this is the right time to do the renaming in this issue, since the other issue will take longer probably.

xjm’s picture

Also I don't see why we need to duplicate the test coverage that PHPUnit hopefully already has for these? They're not our APIs.

xjm’s picture

@mondrake re: #46 I was actually thinking the same thing while working on this, but I just wanted to get it in to stop the pain for people porting their modules to D9 during the porting sprint and through release. However it's been taken out of my hands now apparently to get a simple mitigation in and we're scope-creeping things to provide test coverage for PHPUnit etc. so honestly renaming the trait here seems fine.

However, that means it's becoming even more of an actual API, which, again, I really think deserves independent, decoupled test coverage for the functionality rather than test coverage coupled to the specific current list and to PHPUnit itself.

alexpott’s picture

Re #44 because this does not add or change the logic for skipping deprecations that is tested elsewhere. PHPUnit adds deprecations mid cycle so if we want to support these calls we need to be sure that future versions of these asserts do not trigger other warnings. For example assertAttributeEquals currently triggers 3 warnings. If PHPUnit deprecates something else used in any of the calls then we need to know.

The point about the list changing and therefore we should add our own is a good one. Added that back in.

Renamed the class as #48.

xjm’s picture

It doesn't change the logic for skipping deprecations, but the logic for skipping deprecations could change. This is a new API that triggers the deprecations in a new way, and therefore it could behave differently.

Still also dunno why we are adding deprecation tests of PHPUnit's own code. Do we think PHPUnit lacks sufficient internal test coverage, or what?

longwave’s picture

> Still also dunno why we are adding deprecation tests of PHPUnit's own code

If PHPUnit changes any of the warning messages we will no longer convert them to deprecations, these tests will catch that.

xjm’s picture

+++ b/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php
@@ -0,0 +1,64 @@
+ * This trait exists to allow Drupal 8 tests using PHPUnit 7 and Drupal 9 tests
+ * using PHPUnit 8 to happily co-exist.

Probably should update this text as well: not necessarily just Drupal 8 vs. 9, and generally "multiple versions of PHPUnit" also. Because PHPUnit 9 and 10 and etc. are mostly a D9-only problem; by the time we adopt them, D9's API will be onto at least one new minor and therefore not necessarily compatible with 8.9 anymore.

alexpott’s picture

Tried to update the documentation to account for #52.

I'm not sure it is triggering deprecations in a new way. It's using the same @trigger_error(message, E_USER_DEPRECATION) as everywhere else. And the skipping of such deprecations is tested already by \Drupal\Tests\SkippedDeprecationTest

xjm’s picture

  1. If PHPUnit changes any of the warning messages we will no longer convert them to deprecations, these tests will catch that.

    OK, I guess that makes sense. Note though that we could also provide the same kind of coverage even if they were still warnings. It wouldn't be as slick as an @expectedDeprecation, sure, but it's also 100% possible to test by having a @legacy test that just calls every deprecated assertion/usage pattern. If PHPUnit changed the message then, we'd start getting warnings again and the test would fail.
     

  2. So, given that: I still don't think there's a substantive difference in the testability aside from the one case where we want to expect a warning. For that, I guess that one case is what @longwave's question in #34 would help with. I also couldn't get it to change what happened on the CLI at all.

    It does sound like it's supposed to be configurable via the XML configuration file:
    https://phpunit.readthedocs.io/en/9.0/configuration.html?highlight=--sto...

    The failOnWarning Attribute
    Possible values: true or false (default: false)

    This attribute configures whether the PHPUnit test runner should exit with a shell exit code that indicates failure when all tests are successful but there are tests that had warnings.

    So if we did this:

    --- a/core/phpunit.xml.dist
    +++ b/core/phpunit.xml.dist
    @@ -9,7 +9,7 @@
              beStrictAboutTestsThatDoNotTestAnything="true"
              beStrictAboutOutputDuringTests="true"
              beStrictAboutChangesToGlobalState="true"
    -         failOnWarning="true"
    +         failOnWarning="false"
              printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter"
              cacheResult="false">

    ...then we would no longer fail on warnings. Obviously we don't want to change the whole setting because other warnings should legitimately fail. However, if we could change it conditionally in the one test... This is way outside my expertise though. So maybe there is some way to do that, although it could be a hacky one.
     

  3. Still not super thrilled about losing the test coverage that's independent of existing deprecation messages, though, or the general approach of turning them into deprecations in the first place. For me seeing the yellow W on the command line when I'm running tests gives a much clearer signal that this is a direct problem with the test itself instead of with the code that's being tested. I wonder if or how this might affect various build pipelines as well.

    I did reach out to both @catch and @larowlan and... neither had a strong/clear opinion on which approach to take.

xjm’s picture

Oh and re:

I'm not sure it is triggering deprecations in a new way. It's using the same @trigger_error(message, E_USER_DEPRECATION) as everywhere else. And the skipping of such deprecations is tested already by \Drupal\Tests\SkippedDeprecationTest

Internally within the method, yes. But that's an implementation detail. We shouldn't only test what we know to be the current implementation of the thing and trust that the current implementation will behave the way we expect in all four scenarios just because we wrote what we thought would work that way. That's not robust. I honestly don't know why this is a point of controversy.

alexpott’s picture

Re #54.3 and #55 - #49 added a test that's independent of existing deprecation messages.

+++ b/core/tests/Drupal/Tests/PhpUnitWarningsTest.php
@@ -0,0 +1,91 @@
+  /**
+   * @expectedDeprecation Test warning for \Drupal\Tests\PhpUnitWarningsTest::testAddWarning()
+   */
+  public function testAddWarning() {
+    $this->addWarning('Test warning for \Drupal\Tests\PhpUnitWarningsTest::testAddWarning()');
+  }

[Edit: originally point to #53 as where the test was added but I was wrong]

mondrake’s picture

Issue tags: +Deprecated assertions
mondrake’s picture

Version: 9.0.x-dev » 9.1.x-dev
Status: Needs review » Needs work

I think this should go in 9.1.x first, without the hunks below, to allow consistently renaming of the trait across branches. We should not have the hunks below in 9.1.x because if at any point we will support PHPUnit 9, they will simply fail (unless we put BC shims around).

1.

+++ b/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php
@@ -0,0 +1,64 @@
+    'Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.',
+    'Using assertNotContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringNotContainsString() or assertStringNotContainsStringIgnoringCase() instead.',
+    'assertArraySubset() is deprecated and will be removed in PHPUnit 9.',
+    'assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsArray(), assertIsBool(), assertIsFloat(), assertIsInt(), assertIsNumeric(), assertIsObject(), assertIsResource(), assertIsString(), assertIsScalar(), assertIsCallable(), or assertIsIterable() instead.',
+    'readAttribute() is deprecated and will be removed in PHPUnit 9.',
+    'getObjectAttribute() is deprecated and will be removed in PHPUnit 9.',
+    'The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.',
+    'assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeSame() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeInstanceOf() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeEmpty() is deprecated and will be removed in PHPUnit 9.',
+    'The optional $ignoreCase parameter of assertContains() is deprecated and will be removed in PHPUnit 9.',
+    'The optional $ignoreCase parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.',

2.

+++ b/core/tests/Drupal/Tests/PhpUnitWarningsTest.php
@@ -0,0 +1,91 @@
+  /**
+   * @expectedDeprecation Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.
+   * @expectedDeprecation The optional $ignoreCase parameter of assertContains() is deprecated and will be removed in PHPUnit 9.
+   */
+  public function testAssertContains() {
+    $this->assertContains('string', 'aaaastringaaa');
+    $this->assertContains('STRING', 'aaaastringaaa', '', TRUE);
+  }
+
+  /**
+   * @expectedDeprecation Using assertNotContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringNotContainsString() or assertStringNotContainsStringIgnoringCase() instead.
+   * @expectedDeprecation The optional $ignoreCase parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.
+   */
+  public function testAssertNotContains() {
+    $this->assertNotContains('foo', 'bar');
+    $this->assertNotContains('FOO', 'bar', '', TRUE);
+  }
+
+  /**
+   * @expectedDeprecation assertArraySubset() is deprecated and will be removed in PHPUnit 9.
+   */
+  public function testAssertArraySubset() {
+    $this->assertArraySubset(['a'], ['a', 'b']);
+  }
+
+  /**
+   * @expectedDeprecation assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsArray(), assertIsBool(), assertIsFloat(), assertIsInt(), assertIsNumeric(), assertIsObject(), assertIsResource(), assertIsString(), assertIsScalar(), assertIsCallable(), or assertIsIterable() instead.
+   */
+  public function testAssertInternalType() {
+    $this->assertInternalType('string', 'string');
+  }
+
+  /**
+   * @expectedDeprecation assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.
+   * @expectedDeprecation readAttribute() is deprecated and will be removed in PHPUnit 9.
+   * @expectedDeprecation getObjectAttribute() is deprecated and will be removed in PHPUnit 9.
+   * @expectedDeprecation assertAttributeSame() is deprecated and will be removed in PHPUnit 9.
+   * @expectedDeprecation assertAttributeInstanceOf() is deprecated and will be removed in PHPUnit 9.
+   * @expectedDeprecation assertAttributeEmpty() is deprecated and will be removed in PHPUnit 9.
+   */
+  public function testAssertAttribute() {
+    $obj = new class() {
+      protected $attribute = 'value';
+      protected $class;
+      protected $empty;
+
+      public function __construct() {
+        $this->class = new \stdClass();
+      }
+
+    };
+    $this->assertAttributeEquals('value', 'attribute', $obj);
+    $this->assertAttributeSame('value', 'attribute', $obj);
+    $this->assertAttributeInstanceOf(\stdClass::class, 'class', $obj);
+    $this->assertAttributeEmpty('empty', $obj);
+  }
+
+  /**
+   * @expectedDeprecation The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.
+   */
+  public function testAssertEquals() {
+    $this->assertEquals(['a', 'b'], ['b', 'a'], '', 0.0, 10, TRUE);
+  }
+
alexpott’s picture

In order to allow PHPUnit 9 on Drupal 9 we're going to need the forward compatible shims so I disagree about removing the tests. I think that's one of the reasons why we need the tests - we need to ensure our forward compatible shims work as we want them to.

mondrake’s picture

Status: Needs work » Reviewed & tested by the community

Then this one is good to go IMHO.

mondrake’s picture

BTW, BC shims would be relatively easy for these methods that are no longer present in PHPUnit 9

+++ b/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php
@@ -0,0 +1,64 @@
+    'assertArraySubset() is deprecated and will be removed in PHPUnit 9.',
+    'assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsArray(), assertIsBool(), assertIsFloat(), assertIsInt(), assertIsNumeric(), assertIsObject(), assertIsResource(), assertIsString(), assertIsScalar(), assertIsCallable(), or assertIsIterable() instead.',
+    'readAttribute() is deprecated and will be removed in PHPUnit 9.',
+    'getObjectAttribute() is deprecated and will be removed in PHPUnit 9.',
...
+    'assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeSame() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeInstanceOf() is deprecated and will be removed in PHPUnit 9.',
+    'assertAttributeEmpty() is deprecated and will be removed in PHPUnit 9.',
...
+    'expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9.',

but where PHPUnit 9 keeps the method but changes the signature, things will be more complicated. assertContains on strings will probably require rewriting PHPUnit's Assert class.

+++ b/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php
@@ -0,0 +1,64 @@
+    'Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.',
+    'Using assertNotContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringNotContainsString() or assertStringNotContainsStringIgnoringCase() instead.',
...
+    'The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.',
...
+    'The optional $ignoreCase parameter of assertContains() is deprecated and will be removed in PHPUnit 9.',
+    'The optional $ignoreCase parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.',
alexpott’s picture

@mondrake that's a good point. I think we're in the "we'll do our best" with respect to forward compatibility here. But at least this test keeps us honest and if we have to skip the one where the method if changed on PHPUnit9 then so be it - but at least we'll have it documented and continued to be tested on PHPUnit8. There, is of course an on the other hand, if we leave the warnings as they - they'll get surfaced in contrib / custom projects quicker and therefore resolved quicker and therefore it'll be easier for these projects to support both PHPUnit 8 and 9 when core does.

mohrerao’s picture

Assigned: Unassigned » mohrerao
mohrerao’s picture

Assigned: mohrerao » Unassigned
FileSize
14.92 KB
1.9 KB

Rerolled #53

mondrake’s picture

Status: Reviewed & tested by the community » Needs work

Tests are failing.

mohrerao’s picture

Status: Needs work » Needs review
FileSize
15.26 KB

Fixed previous CI failure.

alexpott’s picture

FileSize
603 bytes

@mohrerao please post interdiffs when you make a change to a patch. Here's one for #66.

Status: Needs review » Needs work

The last submitted patch, 66: 3142267-66.patch, failed testing. View results

mohrerao’s picture

@alexpott, #67 was a reroll #53 which failed in #64. Thats why i didnt add interdiff.

Looking into failed tests now.

mohrerao’s picture

Status: Needs work » Needs review

Tried running tests locally after applying patch and tests are passing. Can some one explain why these failures are coming with Drupal CI?

mondrake’s picture

@mohrerao take a look at the console log of the DrupalCI test run: https://dispatcher.drupalci.org/job/drupal_patches/56228/consoleFull

the error is in the build tests phase: Fatal error: Trait 'Drupal\Tests\Traits\PHPUnitWarnings' not found in /var/www/html/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php on line 52

probably an use statement is missing somewhere

jungle’s picture

Status: Needs work » Needs review
FileSize
416 bytes
182 bytes
15.26 KB

Re #71, Drupal\Tests\Traits\PHPUnitWarning is there. But I can not figure it out why #66 did not pass. So rerolled from #53. But attached a raw-interdiff from #66 as well.

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

Strange, it looks like a case sensitivity issue, given interdiff vs #66

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 72: 3142267-72.patch, failed testing. View results

dww’s picture

Status: Needs work » Reviewed & tested by the community

Drupal\Tests\media_library\FunctionalJavascript\WidgetUploadTest strikes again. :(

mondrake’s picture

Priority: Major » Critical

Bumping to Critical since this is in the critical path to PHP 8.

jungle’s picture

it looks like a case sensitivity issue

< +use Drupal\Tests\Traits\PHPUnitWarnings;
...
> +use Drupal\Tests\Traits\PhpUnitWarnings;

Yes, you're right. PHPUnitWarnings vs PhpUnitWarnings

larowlan’s picture

Was there an issue opened with phpunit to split addWarning into addDeprecation/addWarning as that seems like a longer-term solution here? The use of one method for two concepts is how we got here.

Looking at these two approaches again, I think the trigger_error approach is more consistent with how we handle deprecation elsewhere.

andypost’s picture

Issue tags: +blocker

It's blocker for #3127141: Use PHPUnit 9 for PHP 7.4+, while keeping support for PHPUnit 8.4 in PHP 7.3

Re #78 idea with addDeprecation vs addWarning interesting but looks huge api change, it could need to rename trait too, but I lack of reason to introduce this change... re-using deprecation listener for that also could have wrapper

alexpott’s picture

@larowlan I know there has been some discussion between the Symfony project and PHPUnit project about functionality in PHPUnit bridge. However I couldn't find anything in the PHPUnit issue tracker so I created https://github.com/sebastianbergmann/phpunit/issues/4461

alexpott’s picture

CSPELL: checking all files
core/tests/Drupal/Tests/PhpUnitWarningsTest.php:23:38 - Unknown word (aaaastringaaa)
core/tests/Drupal/Tests/PhpUnitWarningsTest.php:24:38 - Unknown word (aaaastringaaa)
CSpell: Files checked: 10, Issues found: 2 in 1 files

Patch attached fixes this.

  • catch committed ab58ca4 on 9.1.x
    Issue #3142267 by xjm, alexpott, mohrerao, jungle, mondrake, longwave,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

OK I still don't have a preference between the approaches, but @larowlan in #78 slightly tips it in favour of the current patch, so...

Committed ab58ca4 and pushed to 9.1.x. Thanks!

Status: Fixed » Closed (fixed)

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