Problem/Motivation
Drupal 8 should fully support PHP 7. PHP 7 includes the ability to type hint primitives, and explicitly declare the return type of methods:
public function foo(string $bar) : string {
// Implementation here.
}
However, this causes PHPUnit <= 5 to fail because it does not support these concepts. It will fail because <= 5, it cannot create mocks that have these explicit types.
Updating PHPUnit to version 5 does work in theory, but unit tests are littered with deprecation warnings because PHPUnit_Framework_TestCase::getMock() is deprecated, and this is used all over the place in Drupal base unit test classes. Also PHPUnit 5 does not support PHP 5.5. In #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2 we are looking to use PHPUnit 6 to get PHP 7.2 support and this version does not even have a getMock() method.
Proposed resolution
Add a trait that:
- adds a getMock wrapper that calls using MockBuilder on PHPUnit 6.
- adds a createMock wrapper that calls getMock() on PHPUnit 4.
getMock will be @deprecated but won't contain a trigger_error(), see #71
Remaining tasks
User interface changes
None
API changes
API addition of createMock() to Drupal test API.
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #92 | 90-92-interdiff.txt | 1.57 KB | alexpott |
| #92 | 2795317-2-92.patch | 13.02 KB | alexpott |
| #90 | 2795317-2-90.patch | 12.94 KB | alexpott |
| #90 | 87-90-interdiff.txt | 533 bytes | alexpott |
| #87 | 2795317-2-87.patch | 12.97 KB | alexpott |
Comments
Comment #2
dawehnerSadly
createMockdoesn't exist in PHPUnit 4.8, so we would have to convert them all to use getMockBuilder or just go with prophecy directly.For me its not a nice movement that phpunit has such a high PHP dependency. This causes problems for people out there which have some more restrictive version constrains, as you see ...
Comment #3
chriscohen commentedI'm not sure on that. Drupal 8 requires PHP >= 5.5.9, and PHPUnit 5.5 requires PHP >= 5.6 so they're actually pretty close.
The only possible issue is people using PHP 5.5.9 which is beyond end of life.
But this does raise the issue that without increasing Drupal 8's requirements from 5.5.9 to 5.6, it wouldn't be possible to use, say, PHPUnit 5.5.
I appreciate that it's always a balance between keeping things up to date, and stagnating so that Drupal can support all the many people who refuse or are unable to update.
Comment #4
dawehnerWell, its the version supported by most linux distros out there.
#2524432: Suggest PHP 5.6 as the recommended version and linked issues are related to this topic.
Comment #6
hswong3i commentedSimply regex replace all existing
getMock()intocreateMock(), for both 8.3.x and 8.4.x.In between, since
composer updatein 8.4.x is now broken due to symfony 3.3.x released (see #2874909: Update Symfony components to 3.3.*), so here also give an additional change to allsymfony/*and JUST depend on 3.2.x, statically.Comment #7
hswong3i commentedComment #8
hswong3i commentedAnother version that without changes to composer.lock, for distribution (e.g. https://github.com/drustack/drustack-standard) to apply patches.
Comment #9
hswong3i commentedIndividual patches create for drupal/core.
Comment #14
hswong3i commentedOnly change
$this->getMock()into$this->createMock().Comment #18
hswong3i commentedRevoke #14 patches via latest dev
Comment #21
hswong3i commentedRecreate drupal/core patches via https://github.com/drupal/core
Comment #22
dawehnerWe obviously can't do this kind of change as long we require php 5.5 as lowest version. Here is an alternative suggestion, we could make "createMock" available in our version of phpunit, convert all of the cases ...
What are your thoughts about that?
Comment #24
hswong3i commentedThe minimal dependency on PHP >=5.5.9 for Drupal 8.x looks a bit crazy from today point of view, especial as PHP 5.5.x already EOL on 2016-07-21 which was one years ago...
Even PHP 7.0.x will go into Security Update Only since 2017-12-03, which maybe just 1 - 2 months after initial scheduled Drupal 8.4.x released on October...
Moreover, when I am running with
composer updateon Drupal 8.4.x, Doctrine Annotations will be upgraded to 1.5.0 with our current composer.json, which also depends on PHP 7.1.x...IMHO, as mentioned on original request of this issue:
Therefore upgrading our Drupal minimal PHP requirement to 7.1.x and so using PHPUnit 5.x (or even PHPUnit 6.x for the best PHP 7.x support and functionality) whenever our next major/minor release on coming October should be a good idea for answering #22.
Comment #25
dawehnerWell, I simply believe this doesn't matter here :) We have dedicated discussions for that, see #2670966: Warn users of old PHP versions. Just complaining here isn't necessarily constructive.
Indeed, see #2898119: Doctrine/Common 2.8 requires PHP ~7.1
Making big changes isn't easy.
Comment #26
dawehnerI'm sorry, I just realized I haven't uploaded the full patch before.
Comment #27
martin107 commentedRegarding the bulk of the coding standard fails ...
We are at the interface between two coding standards. So I have no problem with the phpcs errors with regard to method arguments
as they are a clear copy of something from another project.
I am ONLY correcting the remaining little coding standard err.
I agree with dawehner's train of through line from #25 ...
Speaking for myself.. I regard any project not using php7 as broken.. but I want to solve our phpunit issue now.
I would like to see this issue get resolved as soon as possible.
Comment #28
lendudeCreating a forward compatibility layer sounds like a good idea here. It will allow us to get existing tests ready before we move to PHPUnit 5.
Updated the trait to have a proper docblock and added some more explanation to the trait. Also converted one instance of getMock to createMock as a proof of concept/test case.
The IS needs an update to reflect the current fix. And we might want to do a follow up to convert getMock to createMock everywhere.
Comment #29
lendudeBleh
Comment #30
dawehnerLet's maybe put an @see on here, so its clear that this is just a wrapper for another function?
Comment #31
dawehnerI could imagine it would be helpful to link from here to the official phpunit docs ... sadly they have splitted up the docs into one changelog per release.
Comment #32
lendudeMoved the explanation from the trait docblock to the method docblock since we might want to add more method to this trait in the future and added the @see
Also had a thought, that it would make more sense to wrap getMock and not createMock, this way all existing tests don't need to be updated to be compatible with PHPUnit 5, they just are. And then we can clean them up at our convenience. Or will this not work?
Took out the test case because all test will now run through the Trait.
Comment #33
dawehnerDon't you want to be able to call to createMock in phpunit 4 environments?
Comment #34
lendudeAs discussed on Slack:
lendude [10:50 AM]
maybe we want to do both, getMock wrapper for BC, createMock wrapper for FC?
dawehner [10:50 AM]
@lendude Oh yeah that makes sense!
so something like this?
Comment #35
dawehnerShould we deprecate getMock here already and point to createMock?
Maybe its also useful in the kernel test ... what do you think?
Comment #36
lendude#35.1 yeah was thinking about that too, lets do it, added a CR for that https://www.drupal.org/node/2907725
#35.2 Less mocking being done there, but yeah we should add it there too.
Delayed action on #31, added a @see to the phpunit release announcement https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-f... so people can see why this is needed at all.
Comment #37
dawehnerNice! I guess this patch will fail due to the deprecation error message. I fear we can't trigger the error for now, given that so many contrib/custom tests will break.
Comment #39
lendudeYeah ok, the trigger_error was excessive. Took it out, and put the test case for createMock back in.
Comment #40
dawehnerCan we create a dedicated test so we can test this trait. Maybe we want to add more later?
Comment #41
jofitzCorrected coding standards errors.
Comment #42
dawehnerComment #43
bircherI like tests.
And I also like to use PHPUnit 5 in my testsuite.
Attached some changes:
* Test for PHPUnit 5 is changed to test for the parent class and not the literal phpunit class.
* invoke the createMock on the parent instead of calling the method statically.
* Added tests.
Comment #44
dawehnerThank you @bircher!
I think we should add some documentation why this is needed: Subclassing of test classes
Comment #45
dawehnerComment #46
bircherrerolled the patch with more comments.
Comment #47
bircherComment #49
bircherno magic...
Comment #50
dawehnerOne nice thing we could do is to upload a test patch which updates phpunit to 5.x and then run the testbot against that version. With that we would kinda prove that the trait actually works as expected.
Comment #51
birchergood idea!
I was testing it in my contrib project development environment and it worked there.
I am not sure I didn't just break all sorts of things with the attached patch.
Comment #54
lendudeLOL, I'd just rolled this too, lets see if mine sticks.
Comment #56
lendudecheckForUnintentionallyCoveredCode has been deprecated in PHPUnit 5, see https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-f...
So that needs to be changed to beStrictAboutCoversAnnotation apparently. Any way to do that in a patch? No idea how the bot builds its phpunit.xml
Comment #57
hswong3i commentedReroll patch from #54, remove changes to composer.lock, for distribution (e.g. https://github.com/drustack/drustack-standard) to apply patches.
Comment #58
lendudeWell I think #54 proves that our BC/FC layer works, none of the fails there have anything to do with that. It just means we need to do some additional work when we actually go to PHPUnit 5, but that's fine and out of scope here.
So I'd say #49 is the patch that needs review, reupping @birchers patch here to have the latest patch in the issue be the one that is under review.
Comment #59
hswong3i commentedReroll patch from #49, also update composer.json with
"phpunit/phpunit": "^5.0"Comment #60
dawehnerNote: The point of this issue is NOT to update phpunit itself. Phpunit 5 requires PHP7, so we can't use that at this given point in time.
Comment #61
mile23PHPUnit 5 requires PHP 5.6, not 7.
Also: #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2
Comment #62
andypostBut core declares PHP 5.5 compatibility https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-f...
Comment #63
mile23@andypost: Yes, but we should be as close as possible to dropping PHP 5.5 at any moment. As @dawehner points out, this issue does not change PHPUnit versions.
Comment #64
mondrakeI think this becomes critical in light of #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2
Comment #65
dawehnerJust a reroll of the right patch :)
Comment #66
lendudeReroll looks good, this Iooks ready to me.
Comment #67
alexpottAnother little one to help us on the PHPUnit upgrade path - #2928249: Introduce a PHPUnit 6+ compatibility layer for Drupal\Tests\Listeners classes
This should trigger a silenced deprecated error.
This is not covered by the tests.
The approach looks sane and will help people move tests to whichever PHPUnit version we end up on.
Comment #68
alexpottI'm going to roll the patch from #67 and also merge in #2928249: Introduce a PHPUnit 6+ compatibility layer for Drupal\Tests\Listeners classes which will also get us ready for PHPUnit 6 as this issue is about 5+
Comment #69
alexpottOn looking at all the work to make the listeners 6+ compatible I decided against rolling it into here. Although to get PHPUnit truly 7.2 compatible we need to do that I think might be better to have narrower scoped issues.
Addressed #67 - I made the loop a little bit easier to understand as well and have added tests that fail when the loop is removed.
Comment #71
alexpottHmmm the deprecation is not going to work unless we put it in the skipped deprecations since PHPUnit calls it itself in \PHPUnit_Framework_MockObject_MockBuilder...
Hmmm and skipping doesn't work either because that logic only fires for KernelTestBase/WebTestBase/BrowserTestBase. So fire the deprecation here but instead open a follow issue to fire a deprecation once core and it's use of PHPUnit does not use it. At least we tried :)
Comment #72
alexpottThis change was incorrect too. Because
class_usesonly return what the current class uses - not it and its parents. So I've reverted the code to #65. I've tried to improve the comment to be clearer about what is actually happening here.Comment #74
lendude@alexpott thanks for taking a look at this. The added test coverage and the updated comment address your feedback, so back to RTBC.
Added the purposefully not setting of the trigger_error() to the IS.
Comment #75
mondrake@alexpott in #2927806-33: Use PHPUnit 6 for testing when PHP version >= 7.2 is using a combined patch including the one here, and he spotted a bug. Since we are targetting a PHPUnit 6 compatibility layer there I think this needs to wait until the various pieces of the puzzle come together
Comment #76
alexpott@mondrake yep you're right...
This is the createMock in PHPUnit 6 so we're going to need to do something a bit more complex with all the args to support that and use getMockBuilder() too.
Comment #77
dawehnerSo we need a separate code path for PHPUnit 5 and 6?
Comment #78
alexpottSo here's a version that copes with the PHPUnit 6 changes to createMock and has been tested with it - see #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2
@dawehner tbh I think we just skip PHPUnit 5 altogether.
Comment #79
mondrakeJust a note if we skip PHPUnit 5 altogether -
phpunit.xml.disthascheckForUnintentionallyCoveredCodeis deprecated in favor ofbeStrictAboutCoversAnnotationin PHPUnit 5.2.0 and was eventually removed in PHPUnit 6.0.Since we'll be jumping from PHPUnit 4.8.36 to 6.5.2, this setting will become stale with no warnings unless we remember to do something about it (add a @todo?)
Comment #80
dawehnerLet's rename it to Phpunit6FCTrait then?
Comment #81
alexpott@dawehner well I'm pretty sure that the code would actually still work on PHPUnit 5 because it has the same MockBuilder class as 4 and 6 but I think you're right that renaming the trait is in order. Maybe to something like PHPUnitCompatibilityTrait? Atm this trait is providing stuff for both PHPUnit 4 and 6 so that you could use createMock() in PHPUnit 4 if you like too. That's actually one of the things I'm kinda unsure about - I like getting getMock() to work on PHPUnit 6 but do we really need to make createMock() work on PHPUnit 4?
Also if we have to support other newer versions of PHPUnit we might not be able to do it in the same trait so I can imagine some funky class aliasing in the future.
Comment #82
dawehnerWell, it would have been a nice transition path for people switching over, but now that we seem to have made the decision to jump on a newer phpunit version anyway, this is indeed less helpful.
Comment #83
alexpottI've done more thinking about #81 - I think we need to provide a createMock() in PHPUnit 4 that works like PHPUnit 6 and not change the behaviour of createMock() in PHPUnit 6 which is what the previous patches are doing. Once we do #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2 then we can improve the test because we actually will be doing testing on different PHP Versions.
Patch attached renames things, completes and fixes the docs and changes the trait to use a
supports()method instead to allow for other forward compatibility methods and to make it easier to grok what's happening.The interdiff is not that useful cause renames.
Comment #84
mondrakeRenaming is good, keeps it open for the future (PHPUnit 7 is out in Feb 2018, and 8 planned for Feb 2019...).
I found supernits below, but in general this seems RTBC to me.
'is' should go away
PHPUnit 6
same
same
Comment #85
mondrakeUpdated CR.
Comment #86
mondrakeComment #87
alexpottAddressing #84
Comment #88
larowlanis this correct?
Comment #89
alexpott@larowlan well we have lots of unit tests with this - like
\Drupal\Tests\simpletest\Unit\TestDiscoveryTestand we have the other more genericTeststoo.Our test groups are a bit of a mess. There are also tests in the group
Test... confusing.Comment #90
alexpott@larowlan mind you the only one outside simpletest module itself is \Drupal\Tests\AssertHelperTraitTest - so perhaps you're onto something. Anyway there is no harm in removing it and this does have nothing to do with Simpletest so one less thing to remove when simpletest is removed.
Comment #91
catchNit, but shouldn't this be 'Tests the', and if so that applies to method summaries for the rest of the file.
I really like the approach in general.
Comment #92
alexpottHere's some updated docs. Thanks for the reviews @catch and @larowlan.
Comment #93
alexpottI've updated the CR for the move to PHPUnit 6 and opened a followup #2929133: Replace all usages of getMock() to replace all our usages of getMock() so we can actually add an
@trigger_error().Comment #94
mile23I suggest that be an issue targeted to Drupal 9.1.0. :-)
Comment #95
larowlanAdding mondrake to commit credits for reviews that helped shape the patch
Comment #96
larowlanQueued testing on 5.5, 5.6 and 7.1.
Are we sure this a feature request?
A feature request can't be critical and a feature request can't go into the stable branch.
Comment #97
bircherWell it started out as a feature request, but with the advent of php 7.2 and its incompatibilities this becomes a bug of course.
Comment #98
mondrakeLooking back in the comments, what about #79? Anything to be done here, followup, nothing :)?
Comment #99
alexpott@mondrake I think that that is part of the issue that makes us use PHPUnit 6... that happens in #2927806: Use PHPUnit 6 for testing when PHP version >= 7.2
Comment #100
mondrakeOK, copied #79 to #2927806-63: Use PHPUnit 6 for testing when PHP version >= 7.2.
Comment #103
larowlanCommitted f320141 and pushed to 8.5.x.
As this is a critical bug, I cherry-picked as f9a4241 and pushed to 8.4.x.
Because we're not adding the trigger_error() here, this deprecation should have minimal disruption.
Published change record.
Updated the postponed issue to indicate this is in, see you there.
Thanks all