eboreum / phpunit-with-consecutive-alternative
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
pkg:composer/eboreum/phpunit-with-consecutive-alternative
Requires
- php: ^8.5
- eboreum/caster: ^3.0
- phpunit/phpunit: ^13
Requires (Dev)
- overtrue/phplint: ^9.7
- phpstan/phpstan: ^2.1.54
- sebastian/diff: ^8.3
- slevomat/coding-standard: 8.28
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README

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:
withParameterSetsInOrderwithParameterSetsInAnyOrder
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:
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
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 removewithConsecutive(andat, 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.:
withConsecutivewillReturnOnConsecutiveCallsorwillReturn
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
- Kasper Søfren (kafoso)
E-mail: soefritz@gmail.com
Homepage: https://gitlab.com/kafoso