PHPUnit

PHPUnit 13

Major Release

The PHPUnit development team is pleased to announce the release of PHPUnit 13. This release introduces new features and modifies or removes existing functionality.

A detailed list of changes can be found here. The most significant changes are discussed below.

OMGWTFBBQ? I must upgrade to PHPUnit 13 immediately!

No, you do not.

Just because PHPUnit 13 has been released today does not mean that PHPUnit 11 or PHPUnit 12 will no longer work. While old versions of PHPUnit will eventually stop receiving bug fixes, we will keep them compatible with new versions of PHP for as long as possible.

We sincerely hope that you do not use the Death Star Version Constraint and therefore do not automatically and unintentionally upgrade to a new major version of PHPUnit.

Upgrading a dependency to a new major release must be a conscious decision that is part of a defined process. This process should include, at a minimum, reading an announcement like this and the ChangeLog.

PHPUnit 13 requires PHP 8.4 or later. If you are not using PHP 8.4 or PHP 8.5, you will not be able to use PHPUnit 13 right away.

We are confident that PHPUnit 13 works as intended, but in some respects it works differently than previous versions.

For example, functionality that was only soft-deprecated in PHPUnit 12 is now hard-deprecated in PHPUnit 13. And functionality that was hard-deprecated in PHPUnit 12 has been removed in PHPUnit 13. You can find details about deprecations here:

You should not even try to upgrade to PHPUnit 13 if you cannot run your test suite using PHPUnit 12.5 without getting deprecation warnings.

If the above does not put you off, then please: go ahead and upgrade! And should you encounter a problem, please report it.

Major Changes in PHPUnit 13

New array assertions

PHPUnit 13 introduces new assertions to address a long-standing need for more precise array comparison semantics beyond the general-purpose assertSame() and assertEquals() methods.

These new assertions operate along three distinct dimensions:

  • comparison strictness (identical using strict type checking versus equal using loose comparison)
  • key consideration (whether array keys must match or only values matter)
  • order sensitivity (whether element order is significant or can be ignored)

This systematic approach eliminates the ambiguity that often arose when developers needed to compare arrays with specific requirements, such as verifying that two arrays contain the same values regardless of key names or order, or ensuring that associative arrays are strictly identical including key ordering.

Each method name encodes all three dimensions directly, making intent immediately clear at the call site. The eight new methods are (#6477):

  • assertArraysAreIdentical() — strict, keys considered, order significant
  • assertArraysAreIdenticalIgnoringOrder() — strict, keys considered, order ignored
  • assertArraysHaveIdenticalValues() — strict, keys ignored, order significant
  • assertArraysHaveIdenticalValuesIgnoringOrder() — strict, keys ignored, order ignored
  • assertArraysAreEqual() — loose, keys considered, order significant
  • assertArraysAreEqualIgnoringOrder() — loose, keys considered, order ignored
  • assertArraysHaveEqualValues() — loose, keys ignored, order significant
  • assertArraysHaveEqualValuesIgnoringOrder() — loose, keys ignored, order ignored

A replacement for withConsecutive()

PHPUnit 13 introduces two new methods as a replacement for the long-removed withConsecutive() (#6455):

  • withParameterSetsInOrder(...) validates that the mock object is called with each parameter set in the exact order configured
  • withParameterSetsInAnyOrder(...) validates that the mock object is called with each parameter set in any order

For an in-depth discussion of these new methods, please refer to this article.

Sealed test doubles

PHPUnit 13 introduces sealed test doubles, a powerful new feature that prevents common mocking pitfalls by finalising a test double's configuration and enforcing strict expectations.

When a test double is sealed using the new seal() method, it disallows any further configuration through expects() or method() calls, ensuring that mock objects are fully configured before the system under test is exercised.

On mock objects specifically, sealing implicitly configures all methods without explicit expectations to reject invocations that were not explicitly configured as expected. This feature addresses long-standing testing challenges where developers inadvertently configured mock objects after using them, or where tests passed despite unexpected method calls because unconfigured methods simply returned null.

Sealed test doubles provide a clear contract: once the configuration is finalised, the test double becomes a reliable, immutable fixture that accurately reflects the intended behavior of dependencies, making tests more robust and maintainable while catching unintended interactions that would otherwise go unnoticed.

seal() is called directly on the mock or stub object after all configuration is complete. Additionally, the requireSealedMockObjects XML configuration option (#6468) can be used to enforce sealing globally across an entire test suite, turning any unsealed mock object into a test failure.

This purely additive feature requires no changes to existing test suites, allowing developers to opt in where stricter test double behavior provides value (#6466).

The any() matcher is now hard-deprecated

The deprecation of the any() matcher reinforces a fundamental principle: mock objects are verification tools designed to assert that specific interactions occur, not to silently ignore them.

By eliminating the conceptual contradiction inherent in creating a mock object while declaring indifference to its invocation, PHPUnit 13 encourages developers to make deliberate choices: either using specific matchers like once() or exactly() for true verification concerns, or migrating to test stubs when no verification is needed.

This shift promotes clearer test intent and eliminates the performance overhead of mock objects in scenarios where they provide no actual verification value (#6461).

When invocation count does matter, use a specific matcher: once(), exactly(N), atLeast(1), atMostOnce(), atMost(N), or never(). When no invocation verification is needed at all, switch from createMock() to createStub() to use a test stub instead.

Supported Versions

Detailed information on supported versions of PHPUnit is available here. Below is a summary as of February 6, 2026:

  • PHPUnit 13 receives bug fixes until February 4, 2028
  • PHPUnit 12 receives bug fixes until February 5, 2027
  • PHPUnit 11 and older versions no longer receive bug fixes

The versions listed above will be kept compatible with new PHP versions as long as possible.

Looking Forward

Here is the roadmap for the next year:

Keep up to date with PHPUnit

You can follow @[email protected] to stay up to date with PHPUnit's development.

You can subscribe to the PHPUnit Updates newsletter to receive updates about and tips for PHPUnit.