Search by

eboreum / phpunit-with-consecutive-alternative

kafosoeboreum

Do you miss the old "withConsecutive" method in PHPUnit? This library fixes that problem for you.

Package info

gitlab.com/eboreum/phpunit-with-consecutive-alternative

Issues

pkg:composer/eboreum/phpunit-with-consecutive-alternative

Statistics

Installs: 3 535

Dependents: 2

Suggesters: 0

Stars: 1

3.0.0 2026-05-30 11:49 UTC

This package is auto-updated.

Last update: 2026-10-10 09:06:27 UTC


README

license pipeline status coverage report PHPStan Level

eboreum-phpunit-with-consecutive-alternative-logo

Do you miss the old "withConsecutive" method in PHPUnit? This library solves that problem for you.

PHPUnit 13 has native support via withParameterSetsInOrder and withParameterSetsInAnyOrder

If you are on PHPUnit 13, please consider using the following methods, which are native to PHPUnit, instead of this library:

  • withParameterSetsInOrder
  • withParameterSetsInAnyOrder

Se reasoning and comment by Sebastian Bergmann under Why was "withConsecutive" removed?.

Installation

Via Composer (https://packagist.org/packages/eboreum/phpunit-with-consecutive-alternative):

composer require --dev eboreum/phpunit-with-consecutive-alternative

Via GitLab:

git clone git@gitlab.com:eboreum/phpunit-with-consecutive-alternative.git

Using this library

Within a test method — i.e. a method inside a child of \PHPUnit\Framework\TestCase — simply do the following:

use Eboreum\PhpunitWithConsecutiveAlternative\MethodCallExpectation;
use Eboreum\PhpunitWithConsecutiveAlternative\WillHandleConsecutiveCalls;

...

$object = $this->createMock(DateTime::class);

$willHandleConsecutiveCalls = new WillHandleConsecutiveCalls();
$willHandleConsecutiveCalls->expectConsecutiveCalls(
    $object,
    'setISODate',
    new MethodCallExpectation($object, 1999, 42),
    new MethodCallExpectation($object, 2000, 43, 2),
);

...


$this->assertSame($object, $object->setISODate(1999, 42));
$this->assertSame($object, $object->setISODate(2000, 43, 2));

Make it easy for yourself

Make your own (abstract) test case class and simply add a proxy method doing the above.

Example:

use Eboreum\PhpunitWithConsecutiveAlternative\MethodCallExpectation;
use Eboreum\PhpunitWithConsecutiveAlternative\WillHandleConsecutiveCalls;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

abstract class AbstractUnitTestCase extends TestCase
{
    /**
     * @param non-empty-string $methodName
     */
    final public static function expectConsecutiveCalls(
        MockObject $object,
        string $methodName,
        MethodCallExpectation ...$methodCallExpectations,
    ): void {
        (new WillHandleConsecutiveCalls())->expectConsecutiveCalls($object, $methodName, ...$methodCallExpectations);
    }
}

⚠️ Notice: We do not hook into \PHPUnit\Framework\MockObject\Builder\InvocationMocker and so — contrary to the original withConsecutive — we place the above method on \PHPUnit\Framework\TestCase instead.

  • In simpler terms: You call a method on \PHPUnit\Framework\TestCase, with the mock as an argument, rather than calling a method on the mock itself.
  • This change is largely because PHPUnit utilizes the "final" keyword a lot on classes and does not support decorators, making extending (an instance of) InvocationMocker nigh-impossible (unless we do evil class-override things).

Why was "withConsecutive" removed?

Sebastian Bergmann, the creator of PHPUnit, has been gracious enough to make us aware that a native PHPUnit replacement for withConsecutive has existed from phpunit/phpunit version 13.

From https://gitlab.com/eboreum/phpunit-with-consecutive-alternative/-/work_items/1:

  1. PHPUnit 13.0, released on February 6, 2026, introduced withParameterSetsInOrder() and withParameterSetsInAnyOrder(). The former covers the original use case of withConsecutive(); the latter verifies that all expected parameter sets were used, regardless of order. As version 3.0.0 of this library requires PHPUnit ^13, all of its users already have both available. I explain them here: https://phpunit.expert/articles/better-than-withconsecutive.html

  2. The article "Do not mock what you do not own" is no longer a 404; it is available at https://thephp.cc/articles/do-not-mock-what-you-do-not-own (the old URL redirects there).

Previously, this section had the following paragraph, which is no longer true for PHPUnit 13.

You can see some of the reasoning and discussion about it here: https://github.com/sebastianbergmann/phpunit/issues/4026

The main reason @sebastianbergmann decided to remove withConsecutive (and at, by the way) was with the argument: Don't mock what you don't > own. Sadly, the resource he links to in https://github.com/sebastianbergmann/phpunit/issues/4026 — being https://thephp.cc/news/2021/04/do-not-mock-what-you-do-not-own — is now a 404 > page. Sebastian is a fantastic person and I and many others greatly appreciate his work over the years. Truly! However, I for one disagree with the "Don't mock what you don't own" > sentiment. If something is part of the public API — third-part or not — one should be able to and allowed to mock it. Period.

No convenient alternative has been provided in PHPUnit itself.

A core problem — fixed

A core problem was the disjointed connection between arguments and their corresponding return values, as they would be provided in separate methods, i.e.:

  • withConsecutive
  • willReturnOnConsecutiveCalls or willReturn

In order to make this connection abundantly clear, the value-object class \Eboreum\PhpunitWithConsecutiveAlternative\MethodCallExpectation has been implemented. Within it is stored a return value (required) and 0 or more arguments.

Notice: You may indeed use callbacks (i.e. \PHPUnit\Framework\TestCase->callback(...)) for the arguments instead of the actual values.

License & Disclaimer

See LICENSE file. Basically: Use this library at your own risk.

Contributing

We prefer that you create an issue and or a merge request at https://gitlab.com/eboreum/phpunit-with-consecutive-alternative, and have a discussion about a feature or bug here.

Branch rules

main = 2.x (not a tag): PHP ^8.5.

Previous branches:

  • 1.x: PHP ^8.2.

Docker PHP image pins

Docker image build argument defaults in docker/php/Dockerfile and docker-compose.yml must remain "invalid" so missing image selections fail early.

Version-specific variables such as PHP_IMAGE_80505_CLI_TAG and PHP_IMAGE_80505_CLI_INDEX_DIGEST pin the supported PHP versions in .env and .gitlab-ci.yml.

The reusable Dockerfile consumes the generic selected-image build arguments PHP_CLI_IMAGE_TAG and PHP_CLI_IMAGE_INDEX_DIGEST. docker-compose.yml maps the version-specific variables into those generic build argument names.

Credits

Authors