Problem/Motivation
PHPUnit 10 deprecates use of non-static @dataProvider methods. This means that @dataProvider methods can no longer make calls to non-static methods. Any non-static call ($this->someMethod()) needs to be replaced by a call to a static alternative (static::someOtherMethod()), that would have to be implemented if not available.
In this issue, focus on replacing usage of PHPUnit mocks in CommentLinkBuilderTest.
From #15 which is a summary of this Slack discussion, there are four options
1. Complete this issue, which introduces a new API, allow checking call count expectations, etc.
2. Drop all the calls to count expectation methods in dataproviders that use prophecy --> but in that case we need to correct other conversions and possibly find a way to prevent them from being called (PHPStan rule?)
3. Drop all the usage of Prophecy and createMock (that are not available statically) and convert all dataproviders to only use createStub - to be investigated if possible and require adjusting any conversion done already
4. Check what other projects with large PHPUnit suites have done or are planning to do about this, if they also ran into it - unsure if there are any though
Proposed resolution
Following up on #3365331: [PHPUnit 10] Provide a static alternative to ConfigMapperManagerTest::providerTestHasTranslatable, replace usage of PHPUnit mocks with PHPSpec prophecies instead, wherever possible.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #24 | 3368713-nr-bot.txt | 1.24 KB | needs-review-queue-bot |
| #17 | 3368713-nr-bot.txt | 90 bytes | needs-review-queue-bot |
Issue fork drupal-3368713
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3368713-alternate
changes, plain diff MR !6661
- 3368713-phpunit-10-provide
changes, plain diff MR !4230
Comments
Comment #3
mondrakeI started off trying to see if we could have a single patch for the conversion, but this is turning out to be quite detailed work, with interconnections. If we go that way this will soon become unreviewable.
So I'm rescoping here to only two test conversions, and I think we'll have to address the rest in one issue per each test converted.
Hope this makes sense.
Comment #4
spokjeChecking 1. for only these two tests was already quite a strain on my mind, so I can agree with splitting this up in to multiple/many issues.
Comment #5
mondrakeNW to check some stuff. Will explain later.
Comment #6
mondrakeI found a problem in this approach - we are not checking call predictions for the prophecies in the dataproviders. See the inline comments.
Essentially,
$contact_form->getRecipients()->shouldBeCalledTimes(500)->willReturn($recipients);does generate a double that can be used in the test and behaves as instructed, butshouldBeCalledTimes(500)does not have any effect, since we are not checking the prediction post-factum. That's because in order to have the prediction checked, the prophet object that generated the prophecy should be available after the prophecy has been used. But in the approach so far the prophet object gets out of scope after the dataprovider is executed. More - dataproviders are executed during test discovery, i.e. well before test execution.Now, the question is: should we support call prediction checking for objects in dataproviders? Probably, a purist would say that dataproviders are there to... provide immutable data for the test execution on the SUT, and the idea itself to test the data clashes with the principle of separation of concerns. But I am not a purist, so I am open to hear differently. If we need to support it, though, the implementation may be very tricky.
For completeness, checked with HEAD, and
yields PHPUnit errors like
However, with PHPUnit 10 and moving dataproviders to static I'd say it would be unlikely support for this will still be there.
Comment #7
mondrakeActually, I found a (relatively) simple way to keep track of the Prophets created for each class with @dataproviders methods, and checking predictions at the end of each class in
tearDownAfterClass(). I like it, it looks like it can make much more robust doubles with this...Comment #8
spokjeLove it!
RTBC for this one, core committers/other big brains will chime in when this is on their RTBC-radar.
However in the already committed #3365331: [PHPUnit 10] Provide a static alternative to ConfigMapperManagerTest::providerTestHasTranslatable we have a
I think/suppose/guess we have to revisit that after agreeing and commiting this, because of the
$nested_element->getIterator()->shouldBeCalledTimes(1)and do the TearDown-Tango there as well?Comment #9
mondrake#8 yes, and #3354382: [PHPUnit 10] Provide a static viable alternative to $this->prophesize() in data providers too.
Comment #10
spokjeAh, didn't even think of that one, adding it as related.
Also, I would like to claim "Data Provider Prophets of Doom" as the name for my next metal band...
Comment #11
mondrake😂
Comment #12
longwaveI'm not convinced we are doing the right thing here. I feel that by making data providers static, the PHPUnit authors are implying that data providers should be fast and not really be doing any work - and to me this includes creating mocks. If we need custom mocks per test combination, I think we should be providing the values required to create the mock into the test method, but not the mock itself?
In two of the cases here we could just adjust the arguments to the test method and then create the mock inside the test, I think that would be easier to follow than adding a new Drupalism to tests.
Comment #13
mondrakeI do not think that's the intent. Otherwise they would not implement something like
in 10.3.0, https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-10.3.md
IMHO, the purpose of static dataproviders is to really separate the test object from the data used by the test. I wouldn't be surprised if in the future dataproviders will have to move to a separate class from their consumers.
In principle I agree though if we can move mocking from the dataprovider to the test it would be better, but I'm not sure this can be fits-for-all cases.
Comment #14
smustgrave commentedSo would next steps be for this issue?
Comment #15
smustgrave commentedThere was conversation between @catch, @mondrake, and @longwave on #contribute in slack. I was just a fly on the wall as this is above my head.
@mondrake proposed
@catch
@longwave
So tagging for follow ups but marking as this may be the best way forward (from my understanding), based on upstream phpunit.
If I paraphrased wrong sorry!
Comment #16
quietone commentedI'm triaging RTBC issues. I read the IS and the comments.
@smustgrave, thanks for recording the conversation from Slack. I do think it is better for all if summaries are in the Issue Summary. Having a link to the Slack conversation would help as well, which is https://drupal.slack.com/archives/C1BMUQ9U6/p1694529624846449
I read that thread and I am not convinced that there is agreement to any of the 4 options.
I am setting this back to Needs Review for make sure we agree on the direction. I will also mention this issue in committer slack.
Comment #17
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #18
mondrakerebased
Comment #19
larowlanWhat was the outcome of #15 - did we do research into how other projects are handling this.
I'm in the same camp as @longwave in #12 - I think we should be passing values in the data provider and create the mocks in the
::testSomethingmethodsOr failing that another option is to return a Closure that is bound to the class. In client projects we do that with Drupal Testing Traits because Drupal APIs aren't yet available in data providers.
I _think_ that would work here
Comment #20
smustgrave commentedNot sure an option was agreed upon. Moving to NW so the issue summary can be updated to include what the decided outcome should be.
Comment #21
quietone commentedChanging to Needs review because this needs an answer on the approach.
@larowlan support @longwave in passing values in the data provider and create the mocks in the ::testSomething methods. See #12.
Comment #23
longwaveTried an alternative approach for CommentLinkBuilderTest in MR!6661.
MailHandlerTest is a bit more messy but I think we could clean it up to make the test more readable at the same time.
Comment #24
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #25
mondrake#23 I think it's better split this issue in two then, one for the CommentLinkBuilderTest fix and one for MailHandlerTest.
Comment #26
longwaveComment #27
longwaveComment #29
mondrakeI think I can RTBC this given it’s a different approach from the previous MR. It’s in line with latest issues of this kind where instead of opening mocks in the dataprovider, we are passing data for the mocks to be created in the test itself.
Comment #30
alexpottCommitted and pushed e021637080 to 11.x and 9ad8446eb0 to 10.3.x. Thanks!