Problem/Motivation

Similarly to #2950132: Support PHPUnit 7 optionally in Drupal 8, while keeping support for ^6.5, this issue is about enabling testing with PHPUnit 8 in Drupal 8.

PHPUnit 6 is out of support since Feb 1, 2019
PHPUnit 7 will be end of support Feb 7, 2020
PHPUnit 8 will be supported until Feb 5, 2021
PHPUnit 9 will be released Feb 7, 2020 - #3110543: [meta] Support PHPUnit 9 in Drupal 9

The big problem with PHPUnit 8 is that it is changing the signature of the main test methods that test classes extend - above all setUp() and tearDown.

This means that ALL the 1700+ implementations of setUp() and the 30 of tearDown() in Drupal test code will have to be touched one way or another, sooner or later.

Also, in PHPUnit8 several assertions methods are deprecated for removal in PHPUnit9, see #3110543: [meta] Support PHPUnit 9 in Drupal 9.

Spun off issues

Done

Proposed resolution

There seems to be three options, we settled on option #3.
3

3. Upgrade to PHPUnit 8 in Drupal 9 but rewrite TestCase

Symfony's simple-phpunit rewrites the phpunit TestCase class to not do the void return types. We can do something similar and load the rewritten class before the autoloader would thereby replacing the PHPUnit class. This would allow us to not change our test base classes so making it easy to maintain tests that work on 8.8, 8.9 and 9.0. See #58 for an implementation of this.

Options 1 and 2 for posterity:

1. Change the signatures of the methods in Drupal test code according to PHPUnit 8

This will have the impact of forcing testing only on PHP 7.1+ (the minimum that allows specifying a void return typehint to methods), and from the day such a change will be in core, all contrib module will have to comply or fail tests.

2. Introduce a compatibility layer

  1. A new Drupal\Tests\BaseTestClass, extending from PHPUnit\Framework\TestCase, is the new base class for ANY Drupal test, being Unit, Kernel, Functional, etc
  2. The Drupal\Tests\BaseTestClass is actually aliased to a Drupal\TestTools\PhpUnitCompatibility\PhpUnit#\BaseTestCase class, one for each PHPUnit version, using the compatibility layer mechanism put in place to support PHPUnit 7. These aliased classes hold the override of the ::setUp(), ::tearDown(), etc methods, type hinted according to the specific PHPUnit version.
  3. Each overridden method calls a corresponding proxy in the test class so ::setUp() -> ::xxxSetup(). The naming tbd.
  4. ALL test classes in Drupal have to rename their PHPUnit overriding methods to the proxies, and any call to parent::method() to the parent proxy, too i.e. parent::setUp() --> parent::xxSetUp()

Remaining tasks

Review.

User interface changes

None

API changes

Some

Data model changes

None

Release notes snippet

Updated to PHPUnit 8.

CommentFileSizeAuthor
#92 3063887-92.patch1.29 KBmondrake
#81 3063887-81.patch28.54 KBalexpott
#81 73-81-interdiff.txt1.4 KBalexpott
#73 3063887-73.patch30.04 KBalexpott
#73 66-73-interdiff.txt957 bytesalexpott
#66 3063887-66.patch29.11 KBalexpott
#66 63-66-interdiff.txt7.03 KBalexpott
#63 3063887-61.patch29.12 KBalexpott
#63 58-61-interdiff.txt9.44 KBalexpott
#58 3063887-58.patch30.54 KBalexpott
#58 56-58-interdiff.txt4.36 KBalexpott
#56 3063887-56.patch27.97 KBalexpott
#56 55-56-interdiff.txt2.23 KBalexpott
#55 3063887-55.patch27.21 KBalexpott
#55 54-55-interdiff.txt4.5 KBalexpott
#54 3063887-54.patch26.52 KBalexpott
#54 53-54-interdiff.txt8.4 KBalexpott
#53 3063887-53.patch18.12 KBalexpott
#53 52-53-interdiff.txt2.42 KBalexpott
#52 3063887-52.patch15.7 KBalexpott
#52 50-52-interdiff.txt2.13 KBalexpott
#50 3063887-50.patch14.44 KBalexpott
#38 3063887-38-for-review-do-not-test.patch31.36 KBmondrake
#38 3063887-38-full.patch1.41 MBmondrake
#35 3063887-35.patch1.41 MBmondrake
#35 interdiff_34-35.txt661 bytesmondrake
#34 3063887-34.patch1.41 MBmondrake
#34 interdiff_33-34.txt17.9 KBmondrake
#33 3063887-33.patch1.41 MBmondrake
#30 3063887-30-full.patch1.43 MBmondrake
#30 3063887-30-for-review-do-not-test.patch36.04 KBmondrake
#27 3063887-27-for-review-do-not-test.patch38.06 KBmondrake
#27 3063887-27-full.patch1.42 MBmondrake
#19 3063887-19-for-review-do-not-test.patch38.69 KBmondrake
#19 3063887-19-full.patch1.42 MBmondrake
#18 3051263-18-full.patch1.44 MBmondrake
#14 interdiff_11-14.txt9.1 KBmondrake
#14 3063887-14.patch394.65 KBmondrake
#11 interdiff_9-11.txt28.81 KBmondrake
#11 3063887-11.patch390.71 KBmondrake
#9 3063887-9.patch405.49 KBmondrake
#7 3063887-8.patch293.82 KBmondrake
#2 3063887-2.patch338.01 KBmondrake

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Status: Active » Needs review
StatusFileSize
new338.01 KB

This patch is buidling on the latest #2950132: Support PHPUnit 7 optionally in Drupal 8, while keeping support for ^6.5 patch to extend support to PHPUnit 8, implementing option 2 from the IS. Just focusing on Unit tests to keep the patch (relatively :)) smallish. PHPunit's methods are implemented in the compatibility traits, and their test class version-independent equivalents renamed to xxxMethod.

mondrake’s picture

Status: Needs review » Postponed
wim leers’s picture

Issue tags: +Drupal 9

The big problem with PHPUnit 8 is that it is changing the signature of the main test methods that test classes extend - above all setUp() and tearDown.

😨

mondrake’s picture

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

mondrake’s picture

StatusFileSize
new293.82 KB
mondrake’s picture

Title: Support PHPUnit 8 in Drupal 8 » Support PHPUnit 8 optionally in Drupal 8, while keeping support for ^6.5 and ^7
Status: Postponed » Needs review

So, here's a new start.

The proposal is:

  1. A new Drupal\Tests\BaseTestClass, extending from PHPUnit\Framework\TestCase, is the new base class for ANY Drupal test, being Unit, Kernel, Functional, etc
  2. The Drupal\Tests\BaseTestClass is actually aliased to a Drupal\TestTools\PhpUnitCompatibility\PhpUnit#\BaseTestCase class, one for each PHPUnit version, using the compatibility layer mechanism put in place to support PHPUnit 7. These aliased classes hold the override of the ::setUp(), ::tearDown(), etc methods, type hinted according to the specific PHPUnit version.
  3. Each overridden method calls a corresponding proxy in the test class so ::setUp() -> ::xxxSetup(). The naming tbd.
  4. ALL test classes in Drupal have to rename their PHPUnit overriding methods to the proxies, and any call to parent::method() to the parent proxy, too i.e. parent::setUp() --> parent::xxSetUp()

The patch here is introducing the thing and converting Unit and Build tests, limiting testing to these test types only in DrupalCI. The size is huge just because it's an all or nothing thing... it's tedious but not complicated.
The patch is also lowering the PHPUnit upgrade from PHP 7.3 to PHP 7.1 so we can test with PHPUnit 6, 7 and 8.

Open problems:

  • ExpectDeprecationTrait - its version in HEAD is not compatible with PHPUnit 8 since PHPUnit 8 has a ::expectDeprecation() method with a different signature than the one in the trait; also, looks like the overall logic underlying is different.
  • MigrateExecutableMemoryExceededTest - it has mock expectation fails in PHPUnit8 due to inconsistent matches(). Added a @todo and commented out the failing tests.
  • ViewExecutableTest - it has mock expectation fails in PHPUnit8 due to callback return type should be bool and not string. Added a @todo and commented out the failing tests.
mondrake’s picture

StatusFileSize
new405.49 KB

the patch

mondrake’s picture

mondrake’s picture

StatusFileSize
new390.71 KB
new28.81 KB

In #8, I misinterpreted the logic behind ExpectDeprecationTrait - its purpose is different than PHPUnit8 expectDeprecation, but we have a method name collision that we need to solve. Here, renaming to xxxExpectDeprecation.

mondrake’s picture

mondrake’s picture

mondrake’s picture

Issue summary: View changes
StatusFileSize
new394.65 KB
new9.1 KB

Rerolled and added protection to prevent circular referencing of main/proxied methods that claims all memory and then fails - added an excpetion instead taht will call to action to rename the main methods in the test classes to their proxies.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Issue summary: View changes
mondrake’s picture

Issue summary: View changes
mondrake’s picture

StatusFileSize
new1.44 MB

Here's a megapatch converting ALL Drupal tests, including Kernel/Functional/JavaScript.

mondrake’s picture

StatusFileSize
new1.42 MB
new38.69 KB

Here's a full patch rerolled, and a patch for review with just the key changes from HEAD - the changes are relatively small, the size of the full patch is due to thousands of renames.

mondrake’s picture

Note: patches in #18 and #19 run as follows:

PHP 7 -> PHPUnit 6.5.14
PHP 7.1 -> PHPUnit 7.5.17
PHP 7.2 and 7.3 -> PHPUnit 8.4.3

chi’s picture

Is there any reason we don't use PHPUnit 8 in Drupal 9 yet? At this moment PHPUnit 8 is the only version that supports PHP 7.4
https://phpunit.de/supported-versions.html

longwave’s picture

Thinking out loud but instead of renaming methods is it possible to implement __call() etc and proxy through to the base class that way rather than directly extending it?

longwave’s picture

Tried a quick hack to see if #22 is viable but PHPUnit's StandardTestSuiteLoader requires test classes to extend TestCase, so we would need to override the loader as well if we go this route.

longwave’s picture

1. Change the signatures of the methods in Drupal test code according to PHPUnit 8

This will have the impact of forcing testing only on PHP 7.2+ (the minimum allowed by PHPUnit8), and from the day such a change will be in core, all contrib module will have to comply or fail tests.

Return type signatures were introduced in PHP 7.0 so I think if we were to add these now then it would work on all supported versions. Overridden methods can specify a return type even if the parent method doesn't: https://3v4l.org/KXAWl

However as you say this is still problematic for anyone in contrib that is extending these tests. Can we announce there will be a BC break in tests in Drupal 9, or 9.1? Contrib can get ready now by adding the return type in preparation, the tests will still work on earlier versions.

Or could we introduce a parallel set of base classes that add the return type? This still runs into problems where contrib extends a concrete test class from core - are there many cases of this, though?

We should have heeded the warnings from PHPUnit earlier, see the Looking Forward section of https://phpunit.de/announcements/phpunit-7.html

mondrake’s picture

@longwave re #24 void return types were introduced only in PHP 7.1, so adding the return typehint in Drupal 8, that still supports PHP 7.0, would lead to failure (see https://3v4l.org/gvBoi).

This issue is for D8. For D9, where min PHP is 7.2, that could be a good idea. But I think in that case we may want to drop PHPUnit 6 first to reduce the compatibility layer, and make PHPUnit 7 the default.

Can we announce there will be a BC break in tests in Drupal 9, or 9.1?

Looks like it will have to be, one way or another. Adding framework and release manager review tags to flag the issue.

chi’s picture

For D9, where min PHP is 7.2, that could be a good idea. But I think in that case we may want to drop PHPUnit 6 first to reduce the compatibility layer, and make PHPUnit 7 the default.

I suppose we have to use PHPUnit 8 because PHPUnit 7 does not work with PHP 7.4, and its support ends on February 7, 2020.
https://phpunit.de/supported-versions.html

mondrake’s picture

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Issue summary: View changes
Issue tags: +PHPUnit 8
mondrake’s picture

Issue summary: View changes
StatusFileSize
new36.04 KB
new1.43 MB
mondrake’s picture

mondrake’s picture

Issue summary: View changes
mondrake’s picture

StatusFileSize
new1.41 MB

Re-roll.

mondrake’s picture

StatusFileSize
new17.9 KB
new1.41 MB

Trying a different approach, allowing BC, for #3094151: ExpectDeprecationTrait is not compatible with PHPUnit 8.

mondrake’s picture

StatusFileSize
new661 bytes
new1.41 MB

The last submitted patch, 33: 3063887-33.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

Assigned: Unassigned » mondrake
Issue summary: View changes
Status: Needs review » Needs work

Disregard #34 and #35. Will re-roll from #33.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.41 MB
new31.36 KB

Rerolled from #33.

xjm’s picture

Regarding:

Can we announce there will be a BC break in tests in Drupal 9, or 9.1?

Looks like it will have to be, one way or another.

Given this statement from the IS:

The big problem with PHPUnit 8 is that it is changing the signature of the main test methods that test classes extend - above all setUp() and tearDown.

If anything I'd want to make the break in 9.0 rather than 9.1 because that's a massive change.

In general, BC breaks due to dependency major version updates are allowable in 9.0, but ideally we would have had deprecations in 8.8 for BC breaks in core test APIs. In https://www.drupal.org/node/2957906 it looks like we provided BC in our test base classes for less drastic differences.

+++ b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
@@ -47,8 +47,8 @@ class SimpletestPhpunitRunCommandTest extends TestCase {
-  public static function setUpBeforeClass() {
-    parent::setUpBeforeClass();
+  public static function xxxSetUpBeforeClass() {
+    parent::xxxSetUpBeforeClass();

(And similar for other methods.)

At first I thought the xxx prefix was debug code until I read the BaseTestCase. Is the xxx naming we're inheriting from upstream? If not I'd prefer something descriptive.

mondrake’s picture

#39: no xxx is just a placeholder for "something descriptive" that I did not take care to think of, see IS.

What's happening upstream is only that some of the methods get a void return typehint in PHPUnit 8:

+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -225,8 +225,8 @@
-  public static function setUpBeforeClass() {
+  public static function setUpBeforeClass(): void {

-  protected function setUp() {
+  protected function setUp(): void {
 
-  protected function assertPostConditions() {
+  protected function assertPostConditions(): void {

-    parent::assertPostConditions();
+    parent::assertPostConditions(): void;

-  protected function tearDown() {
+  protected function tearDown(): void {

and this 'forces' any extending class to comply to the typehint.

The patch here implements option 2 from the IS, which is, to some extent, black magic... TBH I would definitely prefer option 1 i.e. just add the return typehints, thus avoiding any PHPUnit dependent BaseTestCase class and the xxx prefixed methods which would be confusing for developers.

I do not see a problem for D9 core to move in that direction - since it is already only supporting PHP 7.2 and min PHP 7.3 support is on the way. The problem would be, though, in contrib, because all tests implementing the methods above would have to be adjusted for the return type hint which is only supported in PHP 7.1+ (see #25), so modules supporting both D8 and D9 would only be testable on PHP 7.1+.

longwave’s picture

Elsewhere we use "do" as a prefix for wrapped methods that are expected to be overridden, so perhaps doSetUp would work if we have go down the option 2 route.

I would strongly prefer the option 1 route however, as we have to do that eventually, unless we want to maintain this compatibility layer forever?

mondrake’s picture

IMHO we'll have to be pragmatic and accept that 'some' level of compatibility layer we will have to maintain anyway moving forward - for example, assertInternalType() will be removed from PHPUnit 9, and is deprecated in PHPUnit 8 - so we may want to have a FC shim in D9 while calls to assertInternalType() are converted to the recommended PHPUnit methods.

But here, yes, the impact of this return typehinting is huge and so far we could not find a softer BC solution.

longwave’s picture

As Drupal 9 requires PHP >7.1 I wonder if we can:

  • document that return types should be added to test methods
  • introduce the return type in 9.0 or 9.1 in concrete test classes that are not expected to be extended, but leave the base classes for now
  • use reflection to discover contrib and custom tests that have not yet added the return type
  • emit a deprecation/compatibility warning when running these tests
  • in Drupal 9.2/9.3 add the return type to the base classes when contrib has had sufficient warning

As this only affects tests and is inherently only an issue that affects developers rather than end users, perhaps we can get away with this?

mondrake’s picture

FWIW, I tried to #3106802: Add void return typehint to test setUp methods in a contrib module, and results are as expected - pass on D8.8, D8.9 and D9.0 with PHP 7.1+, fail for PHP 7.0.

mondrake’s picture

Issue summary: View changes

Updated IS with the fact that option #1 can also work for PHP 7.1.

mondrake’s picture

mondrake’s picture

Title: Support PHPUnit 8 optionally in Drupal 8, while keeping support for ^6.5 and ^7 » [PP-1] Support PHPUnit 8 optionally in Drupal 9, while keeping support for ^7
Priority: Normal » Major
Issue summary: View changes
Related issues: +#3106918: Drop support of PHPUnit 6 in Drupal 9 because it will never get used anyway
mondrake’s picture

Status: Needs review » Postponed
mondrake’s picture

Version: 8.9.x-dev » 9.0.x-dev
alexpott’s picture

Status: Postponed » Needs review
StatusFileSize
new14.44 KB

Here's an alternative idea based on what simple-phpunit does but customising it for the Drupal. This would allow us to trigger deprecations at some point in the future to add the void typehint and thereby make it much simpler for contrib. We can add these deprecations using reflection in KernelTestBase and BrowserTestBase.

The idea is based on simple-phpunit's re-writing of TestCase.php. Instead of rewriting it in place we re-write it to sites/simpletest and include it before the autoloader has a change to include it. Therefore everything uses our copy.

alexpott’s picture

At the moment the patch in #50 updates everything to PHPUnit 8 but I think that keeping PHPUnit 7 around will be valuable for keeping things simple 8.9.x and 9.0.x fixes. But to do that with testing we need to be able to test PHP 7.3 of PHPUnit7 and PHP7.4 on PHPUnit8 - but unfortunately we're not yet green on PHPUnit8.

alexpott’s picture

StatusFileSize
new2.13 KB
new15.7 KB

Of course run-tests.sh is special :)

alexpott’s picture

StatusFileSize
new2.42 KB
new18.12 KB

And now we need to resolve the deprecations. Let's skip them for now.

alexpott’s picture

alexpott’s picture

StatusFileSize
new4.5 KB
new27.21 KB

This is not the right place for this but I think writing the TestCase all the time is breaking things. So I've tried to stop that.

alexpott’s picture

StatusFileSize
new2.23 KB
new27.97 KB

Fixes some more tests.

Status: Needs review » Needs work

The last submitted patch, 56: 3063887-56.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new4.36 KB
new30.54 KB

I think this fixes the remaining fails.

mondrake’s picture

Title: [PP-1] Support PHPUnit 8 optionally in Drupal 9, while keeping support for ^7 » Support PHPUnit 8 optionally in Drupal 9, while keeping support for ^7
Issue tags: +Needs issue summary update

@alexpott kudos - at some point I fantasized about dynamically rewriting the base test class, but looking at your code I realise I would have never been able to. I guess this should be an option 3 in the IS, so flagging for IS update.

Couple of questions -
1) shall we need to emit a deprecation when the actual class being tested is missing the void return type from the relevant methods? Maybe a point for #3107732: Add return typehints to setUp/tearDown methods in concrete test classes, that would take a different path then.
2) PHPUnit 7 EOL is Feb 7, 2020, in 9 days, when PHPUnit 9 will be released. Would it make sense to jump to PHPUnit 8 (meant to support PHP 7.2, 7.3 and 7.4) directly and drop PHPUnit7 straight away?

alexpott’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update

Updated the issue summary to include this new option.

Re #59.
1. Yes but we can do that in 9.1.x or 9.2.x or some minor in the future - when we're less concerned about 8 and 9 co-compatibilty and support for PHP 7.0
2. When I started out i was thinking there was value in supporting PHPUnit7 in Drupal 9 but now I think option 3 allows to move to PHPUnit8 right now - less moving parts for the win!

longwave’s picture

Yeah, this is a neat approach. When this issue was first opened I tried something vaguely similar with class aliases and messing about with PHPUnit's config and couldn't get it to work, but didn't think of intercepting it at the class loader level.

Agree with moving to PHPUnit 8 now if we can, we might as well as it saves us from having to do it later!

mondrake’s picture

Title: Support PHPUnit 8 optionally in Drupal 9, while keeping support for ^7 » Support PHPUnit 8 in Drupal 9, drop support for PHPUnit 7

Bumped #3094151: ExpectDeprecationTrait is not compatible with PHPUnit 8 to critical as it's become a blocker for this issue.

alexpott’s picture

StatusFileSize
new9.44 KB
new29.12 KB

Removing the PHPUnit7 stuff from the patch because it is unused and moving the code that rewrites the TestCase class to somewhere better.

alexpott’s picture

I'm not sure that #62 blocks this issue per se. I think it becomes a very nice thing to have for 8.9.x (and if we're nice 8.8.x) once this lands.

mondrake’s picture

  1. +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
    @@ -0,0 +1,63 @@
    +    // If the class exists already there is nothing we can do. Hopefully this
    +    // is happening because this has been called already. The call from
    +    // \Drupal\Core\Test\TestDiscovery::registerTestNamespaces() necessitates
    +    // this protection and we only need that call for Simpletest UI so once
    +    // Simpletest is removed we can remove this.
    

    Add a @todo referencing #3075490: Move simpletest module to contrib?

  2. +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
    @@ -0,0 +1,63 @@
    +    // Mutate TestCase code to make it compatbile with Drupal 8 and 9 tests.
    

    compatbile/compatible

  3. +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
    @@ -0,0 +1,63 @@
    +    // Fixes Drupal\Tests\Core\Plugin\DefaultLazyPluginCollectionTest - probably
    +    // should fix the test instead.
    +    class_alias('PHPUnit\Framework\MockObject\Rule\InvocationOrder', 'PHPUnit\Framework\MockObject\Matcher\InvokedRecorder');
    

    Add a @todo and/or a followup?

  4. +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/RunnerVersion.php
    @@ -27,4 +27,43 @@ public static function getMajor() {
    +  /**
    +   * Mutates the TestCase class from PHPUnit to make it compatible with Drupal.
    +   *
    +   * @param $autoloader
    +   *   The autoloader.
    +   *
    +   * @throws \ReflectionException
    +   *
    +   * @internal
    +   *   This should only be called by test running code and this method will be
    

    shoudn't this be removed from RunnerVersion then?

alexpott’s picture

StatusFileSize
new7.03 KB
new29.11 KB

Thanks for the review @mondrake.
Re #65

  1. Thinking about this some more if Simpletest UI moves to contrib it is still going to need this so let's only remove the whole class and not this functionality.
  2. Fixed
  3. I think this is edge case enough to change the type hints. If contrib tests extend LazyPluginCollectionTestBase they could do their own alias if they want to run against both D8 and D9. There's no one using this in contrib afaics (http://grep.xnddx.ru/search?text=LazyPluginCollectionTestBase&filename=)
  4. Indeed it should.
mondrake’s picture

Issue tags: +Needs change record, +Needs release note

I'd be +1 on RTBCing this.

catch’s picture

+++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
@@ -0,0 +1,59 @@
+ *
+ * @internal
+ *   This should only be called by test running code and this class will be
+ *   removed during Drupal 9. If you need to use this class, test for the
+ *   class's existence first. If it does not exist, then all of Drupal 9's tests
+ *   are compatible with the class provided by PHPUnit.
+ */

Should this be 'before Drupal 10'? I'd assume if we're going to add the bc layer we'd keep it throughout 9.x

I would have been fine with the hard break in 9.0.0, but +1 to adding the bc layer here now it's been figured out, one less thing for modules to do in the next couple of months.

However, I would assume we'll still want to do #3107732: Add return typehints to setUp/tearDown methods in concrete test classes in 9.0.x so that core's not relying on the bc layer for its own test coverage.

alexpott’s picture

@catch I don't think we need to do #3107732: Add return typehints to setUp/tearDown methods in concrete test classes now. Firstly because if we change the abstract classes like BrowserTestBase then contrib is back in the same place. I think in 9.1.x or whenever (maybe even now but that's going to create a lot of noise for contrib that I don't think we should make) we should deprecate implementations of

  • PHPUnit\Framework\TestCase::setUpBeforeClass()
  • PHPUnit\Framework\TestCase::setUp()
  • PHPUnit\Framework\TestCase::assertPreConditions()
  • PHPUnit\Framework\TestCase::assertPostConditions()
  • PHPUnit\Framework\TestCase::tearDown()
  • PHPUnit\Framework\TestCase::tearDownAfterClass()
  • PHPUnit\Framework\TestCase::onNotSuccessfulTest()

That don't have a return type. But this should be a special deprecation - one that ends mid cycle. Once that deprecation is over then we should add return types our abstract classes and remove this hack.

alexpott’s picture

If we do the hard break in 9.0.0 then modules that are currently passing on all PHP versions we currently support on Drupal 8.8 cannot also be Drupal 9 compatible. And pretty much every module that has already fixed their deprecations and has test coverage has another round of work to do.

mondrake’s picture

#70 +1000 from a contrib maintainer pov. This patch allow modules currently specifying

core_version_requirement: ^8.8 || ^9

to test on PHP 7.0 through to PHP 7.4 with no major headache.

mondrake’s picture

I was also experiencing the SQLite failure like in #66, in #30, and I think this boils down to the SQLite's Connection destructor

  /**
   * Destructor for the SQLite connection.
   *
   * We prune empty databases on destruct, but only if tables have been
   * dropped. This is especially needed when running the test suite, which
   * creates and destroy databases several times in a row.
   */
  public function __destruct() {
    if ($this->tableDropped && !empty($this->attachedDatabases)) {
      foreach ($this->attachedDatabases as $prefix) {
        // Check if the database is now empty, ignore the internal SQLite tables.
        try {
          $count = $this->query('SELECT COUNT(*) FROM ' . $prefix . '.sqlite_master WHERE type = :type AND name NOT LIKE :pattern', [':type' => 'table', ':pattern' => 'sqlite_%'])->fetchField();

          // We can prune the database file if it doesn't have any tables.
          if ($count == 0) {
            // Detaching the database fails at this point, but no other queries
            // are executed after the connection is destructed so we can simply
            // remove the database file.
            unlink($this->connectionOptions['database'] . '-' . $prefix);
          }
        }
        catch (\Exception $e) {
          // Ignore the exception and continue. There is nothing we can do here
          // to report the error or fail safe.
        }
      }
    }
  }

I was thinking to @ silence the unlink call that should not impact anything at that point.

alexpott’s picture

StatusFileSize
new957 bytes
new30.04 KB

This happens because we have multiple processes with database objects constructed. PhpUnit8 is destructing the TestCase at a different time that PHPUnit 7 - this causes the source database connection attached to the MigrateTestBase to be destructed at a different time. The fix is to check whether the file exists. No need to silence errors.

mondrake’s picture

alexpott’s picture

Discussed this issue with @catch. One of @catch's concerns is the proposal to drop the hack mid 9.x. As @catch points out:

There's only 18 months in-between 9.1.0 and 9.4.0 which will be the last 9.x release (probably/hopefully).

and

Well I mostly want the sudden break to coincide with a major release, whether it's 9.0.x or 10.0.x, but it feels like the hack probably could just survive until 10.0.x unless something completely unforseen happens.

Therefore we agreed that:

leaving it (the hack) in as long as possible, and the possible might not be all the way until 10.0.0 is phpunit or php itself makes it unmaintainable.

Our proposal is to:

  1. Add this change to 9.0.x
  2. Do #3107732: Add return typehints to setUp/tearDown methods in concrete test classes in 9.0.x too - this mitigates the copy and paste brigade. But do not add a deprecation yet or add it to abstract classes. This will break some contrib like Redis that extend for core concrete test classes but if you do that you are always taking a risk and you've tacitly accepted that you might have to fix your tests if core test change.
  3. IN 9.1.x add the deprecation for concrete classes
  4. (Hopefully) in 10.0.x add the return types to the abstract classes but at least we're prepared with the minimum of fuss if PHPUnit 9 or PHP 8 forces this upon us.

Doing the above means that

  • contrib can support Drupal 8.8.x on PHP 7.0 to 7.3 and Drupal 9.0.x on PHP 7.3 (and hopefully 7.4) with the minimum of fuss
  • contrib gets time to adjust to PHPUnit 8 if they've already prepped for D9
  • contrib that is yet to prep to D9 can focus on more meaningful fixes and don't get annoyed by all their tests breaking
catch’s picture

For extra background, since PHPUNIT 6 the releases have been annual in February. So given PHPUNIT 9 is on its way out this month, we're most likely to run into a problem with this in either February 2021 (PHPUNIT 10) or February 2022 (PHPUNIT 11), or when PHP 8 comes out whenever that is. Unless something changes with their development cycle.

Symfony 4.4 stops getting bugfixes in November 2022 and EOL is November 2023, and we're probably trying to release Drupal 10 mid-2022 per #3018653: Decide on Drupal 9's EOL date range (and therefore, Drupal 10's release date range) to keep up.

When PHPUNIT 11 comes out, we'll already be actively retiring this bc layer in the Drupal 10 branch. That makes me think we have a decent chance of being able to maintain this until Drupal 10 without too much hassle since there are not all that many releases to get past before we get there - at least compared to how many we've had to span with Drupal 8.

mondrake’s picture

mondrake’s picture

alexpott’s picture

+++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
@@ -0,0 +1,59 @@
+ * @internal
+ *   This should only be called by test running code and this class will be
+ *   removed during Drupal 9. If you need to use this class, test for the
+ *   class's existence first. If it does not exist, then all of Drupal 9's tests
+ *   are compatible with the class provided by PHPUnit.
...
+   * @internal
+   *   This should only be called by test running code and this method will be
+   *   removed during Drupal 9.

We need to update this with the agreement between me and catch to support this for D9 unless PHP8 or PHPUnit forces our hand.

mondrake’s picture

Issue summary: View changes
alexpott’s picture

StatusFileSize
new1.4 KB
new28.54 KB

Rerolled now that Simpletest has been removed and updated comment with agreement outlined in #75 / #76

mondrake’s picture

Time to RTBC this? I cannot, really...

alexpott’s picture

@mondrake I think we might want to do #3094151: ExpectDeprecationTrait is not compatible with PHPUnit 8 first - because we have to backport that to 8.9.x and 8.8.x too.

alexpott’s picture

But at the same time we could do this and then do that only in 8.9.x and 8.8.x

mondrake’s picture

+1 on #84, IMHO it wouldn't make sense to introduce more deprecation triggers in 9.0.x right now for removal before beta

longwave’s picture

Status: Needs review » Reviewed & tested by the community

This approach looks good, let's get this in and then try to get to PHPUnit 9 if we can?!

catch’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs release manager review, -Needs framework manager review, -Needs change record, -Needs release note

I added a change record at https://www.drupal.org/node/3113653. There are no actual changes here to make as such, but we can re-use that change record for PHPUnit 9 and any deprecations we add in subsequent issues. Already added the obvious issues to the change record too.

Also updated the issue summary to make it clearer what the current plan is.

Untagging for release manager review. Both @xjm and me would prefer to make the hard break in a major release, but the approach here allows that release to be Drupal 10 with a deprecation added beforehand, instead of Drupal 9 with none. If PHPUnit 9 has any nasty surprises we'll find out before release. PHPUnit 10 or PHPUnit 11 might break our bc layer, but if they do we can deal with that when it happens.

Committed 5d457e3 and pushed to 9.0.x. Thanks!

catch’s picture

Not sure why the commit didn't auto-post, but it does show up here: https://git.drupalcode.org/project/drupal/commit/5d4

mondrake’s picture

Added a couple more issues to the CR.

  • catch committed 5d457e3 on 9.0.x
    Issue #3063887 by mondrake, alexpott, longwave, catch, xjm: Support...
mondrake’s picture

Trying running the tests directky from PHPUnit on TravisCI leads to errors like

$ $DRUPAL_PATH/vendor/bin/phpunit $TEST_ARGS
PHP Warning:  file_put_contents(/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php): failed to open stream: No such file or directory in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 49
Warning: file_put_contents(/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php): failed to open stream: No such file or directory in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 49
PHP Warning:  include(/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php): failed to open stream: No such file or directory in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 51
Warning: include(/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php): failed to open stream: No such file or directory in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 51
PHP Warning:  include(): Failed opening '/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php' for inclusion (include_path='/home/travis/drupal8/vendor/pear/archive_tar:/home/travis/drupal8/vendor/pear/console_getopt:/home/travis/drupal8/vendor/pear/pear-core-minimal/src:/home/travis/drupal8/vendor/pear/pear_exception:.:/home/travis/.phpenv/versions/7.4.2/share/pear') in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 51
Warning: include(): Failed opening '/home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/../../../../../../sites/simpletest/TestCase.php' for inclusion (include_path='/home/travis/drupal8/vendor/pear/archive_tar:/home/travis/drupal8/vendor/pear/console_getopt:/home/travis/drupal8/vendor/pear/pear-core-minimal/src:/home/travis/drupal8/vendor/pear/pear_exception:.:/home/travis/.phpenv/versions/7.4.2/share/pear') in /home/travis/drupal8/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php on line 51
PHP Fatal error:  Declaration of Drupal\Tests\UnitTestCase::setUp() must be compatible with PHPUnit\Framework\TestCase::setUp(): void in /home/travis/drupal8/core/tests/Drupal/Tests/UnitTestCase.php on line 38
Fatal error: Declaration of Drupal\Tests\UnitTestCase::setUp() must be compatible with PHPUnit\Framework\TestCase::setUp(): void in /home/travis/drupal8/core/tests/Drupal/Tests/UnitTestCase.php on line 38

maybe the problem is the following piece of code

+++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit8/ClassWriter.php
@@ -0,0 +1,54 @@
+    $filename = __DIR__ . '/../../../../../../sites/simpletest/TestCase.php';
+    // Only write when necessary.
+    if (!file_exists($filename) || md5_file($filename) !== md5($alteredCode)) {
+      file_put_contents($filename, $alteredCode);
+    }
+    include $filename;

is not creating the simpletest directory if it's missing?

mondrake’s picture

Status: Fixed » Needs review
StatusFileSize
new1.29 KB
mondrake’s picture

#92 fixes #91 for me.

Out of curiosity - PHPUnit manages its deprecations as (W)arnings in its CLI run. We do not see those in DrupalCI because they're not captured (#2905007: Allow run-tests.sh to report skipped/incomplete PHPUnit tests would address that).

As an example, running the test suite for --group=Database, we get

$ $DRUPAL_PATH/vendor/bin/phpunit $TEST_ARGS
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Testing 
...............................................................  63 / 302 ( 20%)
...................W........W.................................. 126 / 302 ( 41%)
.....WWW....................................................... 189 / 302 ( 62%)
.........................................................S..... 252 / 302 ( 83%)
..............................S...................              302 / 302 (100%)

Time: 2.97 minutes, Memory: 835.00 MB

There were 5 warnings:

1) Drupal\Tests\Core\Database\ConnectionTest::testDestroy
assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.
readAttribute() is deprecated and will be removed in PHPUnit 9.
getObjectAttribute() is deprecated and will be removed in PHPUnit 9.

/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestResult.php:691
/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestSuite.php:597
/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestSuite.php:597
/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestSuite.php:597
/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestSuite.php:597
/home/travis/drupal8/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:621
/home/travis/drupal8/vendor/phpunit/phpunit/src/TextUI/Command.php:200
/home/travis/drupal8/vendor/phpunit/phpunit/src/TextUI/Command.php:159

2) Drupal\KernelTests\Core\Database\SelectTest::testVulnerableComment
Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.

/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestResult.php:691

3) Drupal\KernelTests\Core\Database\SelectSubqueryTest::testConditionSubquerySelect2
The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.

/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestResult.php:691

4) Drupal\KernelTests\Core\Database\SelectSubqueryTest::testConditionSubquerySelect3
The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.

/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestResult.php:691

5) Drupal\KernelTests\Core\Database\SelectSubqueryTest::testConditionSubquerySelect4
The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.

/home/travis/drupal8/vendor/phpunit/phpunit/src/Framework/TestResult.php:691

WARNINGS!
Tests: 302, Assertions: 1770, Warnings: 5, Skipped: 2.
alexpott’s picture

Status: Needs review » Fixed

@mondrake let’s not re-open this issue for travis come stuff. Let’s discuss that in a separate issue.

pasqualle’s picture

thank you

Status: Fixed » Closed (fixed)

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

xjm’s picture

Issue tags: +9.0.0 release notes