Problem/Motivation

PHPUnit 12.5.0+ is changing the way that mock objects work:

  • Mock objects that do not configure expectations should be remediated. Otherwise, they throw a notice: No expectations were configured for the mock object for \Drupal\comment\CommentManagerInterface. You should refactor your test code and use a test stub instead. Read the strategies for fixing notices to learn how to fix them.
  • $this->any() is deprecated. It was equivalent to having no expectation, in which case the object should be a stub (see the previous bullet) or a more exact number of expectations should be added.
  • with() is deprecated for stubs because it was deemed to be equally pointless. They must be removed or the object must be upgraded from a stub to a mock object.

Steps to Reproduce

  1. composer require --dev phpunit/phpunit:^12 -W
  2. There are a few incompatibilities with PHPUnit 12 in Drupal Core. Apply this patch to resolve those issues. After that, you should be able to run phpunit commands normally. curl https://www.drupal.org/files/issues/2026-01-25/phpunit12.patch | git apply
  3. Run PHPUnit with --display-phpunit-notices --display-phpunit-deprecations to see the notices and deprecations. Example command: ddev exec vendor/bin/phpunit --display-phpunit-notices --display-phpunit-deprecations core/modules/block/tests/src/Unit

Proposed resolution

Do that.

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#8 phpunit12.patch1.81 KBdcam

Issue fork drupal-3561671

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: Refactor tests to use stubs instead of mocks where mocks do not configure expectations » [meta] Refactor tests to use stubs instead of mocks where mocks do not configure expectations
dcam’s picture

Issue summary: View changes
dcam’s picture

Issue summary: View changes

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

dcam’s picture

Issue summary: View changes

Added issues for all Core modules.

dcam’s picture

StatusFileSize
new1.81 KB

How to Review These Issues

A basic review would consist of:

  • It goes without saying, but make sure the tests still pass.
  • Verify that instances of ->expects($this->any()) are removed from the altered classes.
  • Ensure nothing important was deleted during the conversion. This mainly applies to instances where method expectations other than any() were deleted, for example ->expects($this->once()) or ->expects($this->atLeastOnce()). Sometimes this was the easiest way to stop the notices. I tried to avoid doing this and also limit these changes to mocked services that were not the class under test. But it's possible that those expectations are actually critical to verifying the behavior of the tested class.

If you would like to check the PHPUnit 12 output for yourself, then you'll have to do a little extra work on your local environment:

  1. composer require --dev phpunit/phpunit:^12 -W
  2. There are a few incompatibilities with PHPUnit 12 in Drupal Core. Apply this patch to resolve those issues. After that, you should be able to run phpunit commands normally. curl https://www.drupal.org/files/issues/2026-01-25/phpunit12.patch | git apply
  3. Run PHPUnit with --display-phpunit-notices --display-phpunit-deprecations to see the notices and deprecations. Example command: ddev exec vendor/bin/phpunit --display-phpunit-notices --display-phpunit-deprecations core/modules/block/tests/src/Unit
  4. After applying an MR from one of the child issues run the command again. There should be fewer or no notices in the output. Ideally they should all be gone, but there are test classes that extend bases from other modules or components. For instance, #3569415: Convert expectation-less test mocks to stubs - MigrateProcessTestCase fixes problems with Migrate process plugin testing across multiple modules. Also, new commits to Core introduce new notices to fix. Please flag any notices that are not fixed. Just be aware that some of these may be intentional.
dcam’s picture

Strategies for Fixing Notices

  • The most common issue is the use of createMock() or worse getMockBuilder() and then nothing is done with the resulting MockObject. These are easily replaced with createStub().
  • Occasionally the easiest solution is to provide expectations for methods. For example, if ->expects($this->any()) was removed, then it may be replaced by ->expects($this->atLeastOnce()). This strategy isn't viable in a lot of cases, such as when mocks are instantiated in setUp() functions, but aren't used in all of the class's tests.
  • Similar to the previous bullet, sometimes you have to add expectations where there were none before. This strategy can lead to some ugly code. A "dummy" expectation for a mock object may need to be added to multiple methods, just to prevent notices from being issued. This means there is a lot of extra code that is completely valueless. Refactoring the test to prevent this from being needed is preferable, but it may not always be possible.
  • If there is a problematic mock object, for instance one created in a setUp() function that only sets one or two expectations in a large class, check those expectations. The expectations may be a "nice to have" and not critical the functionality being tested. In those cases, the expectations might be removed. When an expectation is removed (other than any()), reviewers should be extra careful to validate the change.
  • In some cases mocks can be replaced with concrete instances of a class, constructed with mocked parameters. In practice this tended to happen a lot with tests for plugins and base classes. In the latter case, mocks were previously set up to allow testing of abstract base classes which normally can't be constructed. To get around that limitation, a concrete stub class extending the base may be written.
mondrake’s picture

Great work, @dcam.

mondrake changed the visibility of the branch 3561671-refactor-tests-to to hidden.

dcam’s picture

Issue summary: View changes
longwave’s picture

when mocks are instantiated in setUp() functions, but aren't used in all of the class's tests.

I just read https://phpunit.expert/articles/the-stub-mock-intervention.html and there is a solution to this: the #[AllowMockObjectsWithoutExpectations] attribute (which if we use it, should be used sparingly for this kind of situation - probably only where setup is done in a base class, and we don't know how subclasses will use it?)

dcam’s picture

If I'm not mistaken, we can't use #[AllowMockObjectsWithoutExpectations] until we're actually using PHPUnit 12.5. So my focus has been entirely on fixing the notices without ignoring some instances with the attribute.

dcam’s picture

Issue summary: View changes
dcam’s picture

Issue summary: View changes
dcam’s picture

smustgrave’s picture

That's 9 in RTBC, lets see if those land

dcam’s picture

Issue summary: View changes
dcam’s picture

We are SO CLOSE to being done with this project. Figuratively speaking, my pencil is down. I've found all of the notices that I'm able or willing to find at the moment. There may be some in Functional tests or areas of Core where there are tests of which I'm not aware. Those can all be found by the PHPUnit 12 upgrade issue.

quietone’s picture

Issue tags: +12.0.0 beta blocker
dcam’s picture

Status: Active » Fixed

All child issues have been fixed.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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