Problem/Motivation

PHPUnit 13 was released in Feb 2026.

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3574681

Command icon 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:

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Title: Introduce support for PHPUnit 12 » Introduce support for PHPUnit 13
mondrake’s picture

Issue summary: View changes

catch’s picture

For #3527936: Introduce support for PHPUnit 12 we fixed all the deprecations in advance of the update, which was a lot of work.

For PHPUnit 13, without knowing the scale of the work we need to do, I'm wondering if we should take the inverse approach - skip as many deprecations as we can, so we can update the constraint - the idea being that if we do this, it will be possible for contrib to test against phpunit 13 earlier. Then we can work on removing the skips. Depends on whether this is even a viable approach though.

dcam’s picture

I predict that the big issue we'll face going to PHPUnit 13 is the deprecation of with() for mock objects that do not set an expectation. I tried to correct as many of them as I could during the PHPUnit 12 prep, but that was entirely a manual effort. They did not raise any notices in PHPUnit 12. So it was on me to locate and fix them. What's worse is that I undoubtedly created a some, especially in the early days of the project before the deprecation happened. I caught a lot on re-reviews of uncommitted work, but without checking I feel certain some went in.

mondrake’s picture

Before we jump into this, I wanted to share a few considerations. I am not jumping to conclusions or recommendations, but I think we need to discuss this a bit.

1) There is no way to selectively skip deprecations/notices generated by PHPUnit. These use PHPUnit's internal event system, not the trigger_error(..., E_USER_DEPRECATED) mechanism for which we can intercept the errors in our own error handler before PHPUnit. PHPUnit deprecations/notices can be configured not to fail the tests (via the command line or the configuration file), but it's all-in or all-out.

2) PHPUnit makes changes to its internals and adds deprecations within its latest major. On one side, this leads to minors that require additional patch releases to stabilize (PHPUnit has no alpha/beta releases). On the other side, this leads to deprecations that have quite an impact on large project like Drupal, that require time and work broken down in multiple tickets to address the changes (see the recent mock-to-stub work, deprecations appeared on the last minor of PHPUnit 12, it took months to fix... and before that move, the bump to PHPUnit was practically ready to go). In other words, targeting the cutting edge may mean sudden deprecations to be dealt with on a large scale, and higher risk to hit PHPUnit's own bugs.

mondrake’s picture

Status: Postponed » Needs review

NR to trigger a discussion on #7.

catch’s picture

Discussed this with @mondrake and @Taran2L in slack a bit.

Short version is:

phpunit will continue to add new deprecations to the 13.x branch until December this year, also early 13.x patch releases often have bugs in.

So we should consider specifically staying on PHPUnit 12, and then update to PHPUnit 13's last minor release at the end of the year.

At this point PHPUnit 14 will be releasing, but given PHPUnit has a one major year cycle and Drupal has a two year major cycle, we can always try to update to the 'last minor of the most recent major'.

Occasionally we might get stuck (e.g. Drupal 11 is stuck on PHPUnit 12 due to dropping support for test annotations), but we can't predict that in advance.

If we follow that, then the result here would be postponing this issue until November/December and releasing Drupal 12.0 on PHPUnit 12.

I have one extra question here - whether we should lock to PHPUnit 12 but allow PHPUnit 13 so that we can e.g. run the updated dependencies job to catch new deprecations, but that could also end up being more work compared to doing it when it's all settled.

mondrake’s picture

in order to allow 13, and not fail in case of deprecations/notices, we would have to remove the configuration entries that lead to test failure in that case. or have a version-dependent configuration file

catch’s picture

OK that sounds sufficiently fiddly that we could just wait for the last minor and concentrate the work.

mondrake’s picture

I think the summary is:

  • generally, we keep targeting bump of PHPUnit major to when it's at its last planned minor (for 13, we target to have bump to 13.5 in December)
  • unless something requires a deviation and anticipation of the bump, that will be evaluated case by case

how does it sound?

mondrake’s picture

BTW, this does not mean that in the meantime we can not have separate issues to work on deprecations already identified - for example, the MR here spots the Using with*() without expects() is deprecated instances that I think can be already fixed under PHPUnit 12

catch’s picture

#13 is also a good point and means we can still spread the work out over time without actually changing the constraint in HEAD.

dcam’s picture

In terms of easing the transition: we need to commit more things for upcoming versions sooner. I wish I had done this much earlier in the mock/stub fixing process. For some reason I didn't think of it until I was nearly done and then there was little point. But you all saw we had that phpunit12.patch file that had to be applied if you wanted to run the tests locally. It contained the basic changes that were necessary for getting version 12 to work, __construct() override removals and the v12 TestCompatibilityTrait.

I realize that the constructor deletions were a one-time thing. And someone, probably @mondrake, said it's time to get rid of the traits. My point is that there may be things like that with every new version. Doing these things shouldn't wait until it's time to upgrade the dependency if at all possible. They are a barrier to getting the work done in the first place. They're effectively issue blockers unless you know what you're doing. They should be given Major priority at a minimum and be worked on and committed with haste.

dcam’s picture

how does it sound?

#12 This sounds reasonable to me.

andypost’s picture

maybe it could have a weekly PHPUnit 13 pipeline?

mondrake’s picture

#17 maybe we could put a scheduled pipeline behind the MR here. That would start failing anytime a dependency is updated, though.

mondrake’s picture

BTW @andypost any plan to have a PHP 8.6 image for testing? likewise the PHP 8.5 one that was opened more or less this time one year ago?

andypost’s picture

Good idea will cook one tonight!

andypost’s picture

@mondrake pushed initial image and scheduled reduild 00 12 * * 5 Etc/UTC - there's already internal changes so extensions are patched, also first alpha planned to July 2 https://wiki.php.net/todo/php86

ref https://git.drupalcode.org/project/drupalci_environments/-/jobs/9176064

mondrake’s picture

smustgrave’s picture

Status: Needs review » Needs work

Think everyone has been a +1 to this idea. I joked that we just got 12 in and already going for 13?!?

Only moving to NW as there are some test failures.

mondrake’s picture

Status: Needs work » Postponed

“Postponed” seems more accurate to me.

catch’s picture

Title: Introduce support for PHPUnit 13 » [Nov 2026] Introduce support for PHPUnit 13

Adding the date in the issue title.

mondrake’s picture

Title: [Nov 2026] Introduce support for PHPUnit 13 » [Nov 2026] Introduce support for PHPUnit 13.5

… and the PHPUnit minor version to target.

mondrake’s picture

PHPUnit 13.2 was released today.

  • it introduces a sweeping change, expectExceptionMessage was replaced by either expectExceptionMessageIs or expectExceptionMessageIsOrContains
  • more problematic, it seems it changes the way error handlers are set, and that's breasking our own DeprecationHandler
mondrake’s picture

Looks like 13.2 disattends the fact that we set an error handler in bootstrap.php.

What we are doing is we set the \Drupal\TestTools\ErrorHandler\BootstrapErrorHandler which itself falls back to PHPUnit's own one. So we can exclude the ignored deprecations (those ignored via the ignore file) and return to test execution without even letting PHPUnit know that a deprecation was triggered.

13.2 reverses that - it always sets its own error handler as the main one, and only falls back to \Drupal\TestTools\ErrorHandler\BootstrapErrorHandler after the error (including deprecation ones) have been already collected. So the entire 'deprecation ignore' process is browen.

mondrake’s picture

Please review #3589108: Refactor DeprecationHandler into a standard PHPUnit extension - that one will be blocking this one as we need to get rid of large portions of DeprecationHandler as of PHPUnit 13.3.