Problem/Motivation
In #3432171: dump() no longer produces output in PHPUnit tests running under PHP 8.3 we fixed dump() no longer producing output in PHPUnit tests running in isolation under PHP 8.3, by changing writing to STDERR instead of STDOUT.
However, the fix is not sufficient as later #3452269: dump() calls in tests are producing a PHPUnit\Framework\Exception reported that dump() in kernel tests leads to test failures.
In this issue for PHPUnit 10+, see if we can adopt a different technique instead of using standard streams.
Proposed resolution
Take clues from HtmlOutputLogger and #3453341: Bootstrap HtmlOutputLogger from phpunit.xml, and introduce a PHPUnit extension that captures the output of dump() during the tests, and prints it cumulatively at the end of the testrunner execution. In this, enhance the experience to also add information about where each dump() call happened.
The bigger hurdle here are processes run in isolation (many unit ones, and all kernel/functional/functionaljavascript ones): there's no easy way to return information from the 'isolated' test back to its parent testrunner. However, using an environment variable and a staging file likewise HtmlOutputLogger, we can achieve that.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Please at this issue at the top of the release notes. To make dump() work you need to update your local phpunit.xml file with the following code:
<extensions>
....
<!-- Debug dump() printer. -->
<bootstrap class="Drupal\TestTools\Extension\Dump\DebugDump">
<parameter name="colors" value="true"/>
<parameter name="printCaller" value="true"/>
</bootstrap>
</extensions>
| Comment | File | Size | Author |
|---|
Issue fork drupal-3436029
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:
- 3436029-phpunit-10-only
changes, plain diff MR !8345
Comments
Comment #2
dineshkumarbollu commentedHi @mondrake
Any reason for postpone of issue,
Looking for different technique, will be good if the status is Active. Any Thoughts on this.
Comment #3
mondrake@dineshkumarbollu see #3432171-13: dump() no longer produces output in PHPUnit tests running under PHP 8.3 for the reason.
Comment #4
longwaveThe parent was committed but not sure there is anything worth fixing here, I think stderr is a fine place to put debugging information during tests, unless PHPUnit 10 is even more restrictive in some way.
Comment #5
mondrakeMy idea: during the test execution, make
TestVarDumperstore the dump strings in an array instead of outputting it (so we do not pollute SUT output with test output).Then, after test end (or testrunner end), subscribe to PHPUnit events and print the dump - similar to what we are doing for the HTMLOutputLogger.
That would make it more aligned to PHPUnit architecture.
Waiting for PHPUnit 10 though, where the event system is implemented. Trying it before would mean to touch listeners which are deprecated in PHPUnit 9 and gone in 10.
Comment #6
longwavePersonally I find it more valuable to get immediate debug feedback as the test runs rather than wait until the end - and if somehow you crash the test or the test runner, is there a chance the captured output is lost? I guess this needs to still be postponed until we are on PHPUnit 10, in any case.
Comment #7
mondrakeWell hopefully the post test execution events should still run even if the test itself crashes, otherwise we'd have a problem in PHPUnit. Anyway, let's see what we can do once the right time comes.
Comment #8
mondrakeComment #9
mondrakeComment #10
mondrakeComment #12
mondrakeComment #13
mondrakeComment #14
smustgrave commentedSorry if I missed something but can the issue summary be updated then reviewed?
Comment #15
mondrakeUpdated IS
Comment #16
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 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 #17
mondrakeComment #18
joachim commentedFirst time I'm looking at this, and wow, totally new approach! I guess this is required because of changes in PHPUnit 10?
This needs a CR to explain that phpunit.xml files need to be updated.
I like the printing of the file and line number, though in some instances that might get noisy.
For example, if I'm developing a migration and I want to see the value that's computed on each processing of a row. I'm going to add a single dump() statement and what I want to see is a monotonic list of values:
I want 'somethingbad' to stick out in the list, and the list being simple is important to being able to do this. Filenames and line numbers will hamper this. (Generally, debugging something on a foreach loop is a scenario where I find dump()ing so much easier than using the debugger, as I can see all the values at a glance rather than keep advancing from a break point for each loop iteration.)
> Personally I find it more valuable to get immediate debug feedback as the test runs rather than wait until the end -
Same! Though I appreciate there may not be a way to do that with this approach.
The testVarDump() test outputs the dump statements it is testing. Is there a way of capturing the dump() output from testVarDump(), like it used to be?
I suspect some test runners will spot dump() output and not like it. It's also going to be noise in the test results from d.org CI.
Also, if the DebugDump extension it not registered, the testVarDump() test fails.
> public static function setDebugDumpHandler
The docs here could do with a @see to a fully-qualified DebugDump
> + * Drupal's extension for printing dump() output results.
Say it's a PHPUnit extension.
> self::$stagingFilePath = tempnam(sys_get_temp_dir(), 'dpd');
Could we use the VFS here? Would that be quicker?
> and if somehow you crash the test or the test runner, is there a chance the captured output is lost?
I think it's more likely that the file won't get cleaned up from the tmp directory. Though if we switch to using the VFS then that is solved.
Comment #19
mondrakeNo VFS, sorry. In tests run in process isolation, the VFS instance will only be visibile to the child process, and then destructed before returning to the main process. Or viceversa, if visible to the main process, it won't be visible to the children.
We could add a parameter in the extension configuration in the xml file to opt-out from printing the whereabouts of the dump() call.
Comment #20
joachim commentedAhh right. That's a shame!
Parameter in the extension config -- perfect! :)
Comment #21
mondrakeWell yes and no. There were already changes in PHPUnit 9 that forced to change the current solution to print to STDERR instead of STDOUT. The real problem is that, to an extent, test code and SUT code share the same standard streams. Quite rightly IMHO, especially if tests need to run in isolation, PHPUnit is 'sealing' more and more the SUT to prevent confusion between SUT and test output.
Comment #22
mondrakeAdded
<parameter name="printCaller" value="true"/>as a config parameter for the extension.
Comment #23
joachim commented> There were already changes in PHPUnit 9 that forced to change the current solution to print to STDERR instead of STDOUT. The real problem is that, to an extent, test code and SUT code share the same standard streams
So is output to STERR still possible, even if not always desirable?
If so, could we add an option for it?
Comment #24
mondrakeNo, we changed from STDOUT to STDERR already, and the newly reported failures in kernel tests are related to this.
Comment #25
joachim commentedIf we want 'live' output, would it work to `tail -f` the file in which the output is getting sent to? If so, it would be useful to document that solution.
Comment #26
mondrakeWell that would mean writing to STDOUT, so back to square one. This issue is, fundamentally, avoiding to use standard streams - just capture the output of dump in a temp file, and post-test-mortem, print it via the standard streams of the CLI testrunner that are no longer being checked by PHPUnit for unexpected output. No live output, in this case, sorry - it's the design of the MR itself that prevents it.
Note that if you call
dump()from a component test that extends directly from PHPUnit'sTestCase, or from a Drupal unit test case not run isolation, without the extension being active, then yes, you got exactly that - live dumps, and for free since there is not even the need to do anything.This entire issue is about tests run in isolation. It's process isolation (and all kernel+functional tests, plus a few Drupal unit tests, are executied in isolation) that causes the dump() failures.
At least, that's my understanding - but I'd love to be proven wrong :)
Comment #27
joachim commented> Well that would mean writing to STDOUT, so back to square one. This issue is, fundamentally, avoiding to use standard streams - just capture the output of dump in a temp file,
No, you don't need STDOUT. If the temp file is readable by a second terminal window, then `tail -f` outputs the end of the file *LIVE* as it is being written to. `tail -f` is particularly useful for watching log files, but would work here too, as long as the file is accessible and readable.
Comment #28
mondrakeInteresting. If I understand your idea, that would mean running two separate terminals, one running PHPUnit CLI, and the other the tail command? That would pose the problem of knowing in advance the filepath of the temp file across both terminals, and of course of avoiding the hashing of the file content that the MR is currently doing.
Comment #29
joachim commented> If I understand your idea, that would mean running two separate terminals, one running PHPUnit CLI, and the other the tail command?
Yup.
I wouldn't want that to be the main intended mode of operation, but it would be useful in some circumstances -- such as something that crashes PHPUnit so badly the output is not printed.
Comment #30
mondrakeThought about this, and IMHO dumping to a file for it to be tracked in real time via
tail -fwould better be done with an alternative extension, rather than trying to fit both behaviors in one.Comment #31
mondrakemerged with head
Comment #32
smustgrave commentedCan the CR be written?
Comment #33
mondrake#32 yes I will look into it
Comment #34
mondrakeAdded draft CR
Comment #35
smustgrave commentedCR looks good to me and helps me understand more what's going on.
May be one of those things easier to see once merged so will give it a +1 but will leave in review for others more advanced in phpunit her.e
Comment #36
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 #37
smustgrave commentedFalse positive
Comment #38
mondrakerebased
Comment #39
mondrakeComment #40
daffie commented@mondrake: I am testing the MR on my local machine and there the tests are failing. I am using a DDEV environment.
ddev exec -d /var/www/html "./vendor/bin/phpunit -c ./core/phpunit.xml ./core/tests/Drupal/Tests/UnitTestCaseTest.php"results in:ddev exec -d /var/www/html "./vendor/bin/phpunit -c ./core/phpunit.xml --filter testVarDump ./core/tests/Drupal/KernelTests/KernelTestBaseTest.php"results in:ddev exec -d /var/www/html "./vendor/bin/phpunit -c ./core/phpunit.xml --filter testVarDump ./core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php"results in:Maybe I am doing something wrong. Can you help me?
Edit: Found the solution. I forgot to update my phpunit.xml. I will review the MR.
Comment #41
daffie commentedAll the code changes look good to me.
I have run the tests for the new version of
dump()on my local machine and it work.The CR is in order and I should have read it earlier.
My suggestion is to put this requirement at the top of the release notes!!!
When you do not update your local
phpunit.xmlfile, the function just does not do anything. It will take some time to find out that you need to update your localphpunit.xmlfile. I have updated the IS for this.For me it is RTBC.
Comment #42
quietone commentedI corrected spelling in the CR and other minor tweaks. Updating credit.
Comment #43
mondrakerabased for baseline conflicts
Comment #44
quietone commentedI read the IS and the comments. I didn't find any unanswered questions or other work to do. Tweaked the change record some more.
Comment #45
catchI keep looking at this issue and then closing the tab eventually, because it feels like a lot of work and disruption to fix the issue. But also, I don't have any alternative solution and it is annoying when a test fails only because of a dump() call - makes it harder to spot when you've fixed it.
So I think we should go ahead here - will do so in a couple of days if no-one turns up with a way to avoid the phpunit.xml change in the meantime.
Comment #46
mondrake😂 understand the feeling...
Comment #47
mondrake... on the other hand, I used this several times in the last months and it turned to be very useful and stable to me.
Comment #49
catchLet's go ahead here. I linked to this issue and the CR in #core-development on slack for additional visibility.
Committed/pushed to 11.x, thanks!