seec/phpunit-consecutive-params
Bring back PHPUnit’s removed withConsecutive behavior. This lightweight dev helper provides a drop-in replacement via a trait, letting you assert different parameter sets across consecutive mock calls using ->with(...$this->withConsecutive(...)).
Installation:
composer require --dev seec/phpunit-consecutive-params
Add the trait to your test class:
use SEEC\PhpUnit\Helper\ConsecutiveParams;
First Use Case:
Replace withConsecutive() calls in legacy tests or new mock expectations:
$mock->expects($this->exactly(3))
->method('process')
->with(...$this->withConsecutive(
['input1', 'output1'],
['input2', 'output2']
));
Where to Look First:
withConsecutive() usage (PHPUnit 9+ removed this natively).Mock Expectations:
Use withConsecutive() for sequential method calls with varying parameters:
$mock->method('handle')
->withConsecutive(
[$this->equalTo('user1'), $this->anything()],
[$this->equalTo('user2'), $this->isType('array')]
);
Integration with PHPUnit Constraints: Combine with PHPUnit matchers for dynamic assertions:
$mock->expects($this->atLeastOnce())
->method('validate')
->with(...$this->withConsecutive(
[$this->stringContains('test')],
[$this->logicalNot($this->stringContains('fail'))]
));
Data-Driven Tests: Generate test cases from arrays:
$testCases = [
['input' => 'a', 'expected' => 'A'],
['input' => 'b', 'expected' => 'B'],
];
$mock->method('transform')
->withConsecutive(...array_map(
fn($case) => [$case['input']],
$testCases
));
Legacy Test Migration:
Replace deprecated withConsecutive() in existing tests:
// Before (PHPUnit <9)
$mock->withConsecutive(...);
// After (with trait)
$mock->with(...$this->withConsecutive(...));
Service Container Mocking: Test dependency injection sequences:
$container = $this->createMock(Container::class);
$container->method('make')
->withConsecutive(
['App\Services\UserService'],
['App\Services\Logger']
);
Event Listeners: Verify event dispatch order:
$dispatcher = $this->createMock(Dispatcher::class);
$dispatcher->expects($this->once())
->method('dispatch')
->withConsecutive(
[$this->isInstanceOf(UserRegistered::class)],
[$this->isInstanceOf(UserLoggedIn::class)]
);
Queue Workers: Test job processing sequences:
$queue = $this->createMock(Queue::class);
$queue->method('push')
->withConsecutive(
[$this->isInstanceOf(SendEmailJob::class)],
[$this->isInstanceOf(ProcessPaymentJob::class)]
);
Parameter Spread Operator:
... before withConsecutive() causes runtime errors.with(...$this->withConsecutive(...)).Strict Typing:
returnType() or returnSelf() constraints for strict mocks:
$mock->method('getData')
->willReturnOnConsecutiveCalls(
$this->returnValue('string'),
$this->returnValue(['array'])
);
Order Sensitivity:
withConsecutive() enforces strict call order. Reordering parameters breaks tests.with() for order-agnostic assertions if sequence doesn’t matter.IDE Support:
withConsecutive() as undefined.@mixin annotation to test classes:
/** @mixin \SEEC\PhpUnit\Helper\ConsecutiveParams */
Assertion Failures:
var_dump() to inspect parameter arrays:
var_dump($this->withConsecutive([...]));
Performance:
withConsecutive() results:
private $consecutiveParams = null;
protected function getConsecutiveParams(): array {
return $this->consecutiveParams ??= [
['param1', 'param2'],
// ...
];
}
Configuration Quirks:
phpunit.xml uses PHPUnit 9+:
<phpunit bootstrap="vendor/autoload.php">
<extensions>
<extension class="SEEC\PhpUnit\Helper\ConsecutiveParams"/>
</extensions>
</phpunit>
Custom Matchers: Extend the trait for domain-specific assertions:
trait CustomConsecutiveParams extends ConsecutiveParams {
protected function withConsecutiveEmails(array ...$emails): array {
return array_map(
fn($email) => [$this->isInstanceOf(Email::class)->and($this->equalTo($email))],
$emails
);
}
}
Dynamic Generation: Generate parameters from database fixtures:
$users = User::all();
$mock->method('processUser')
->withConsecutive(...array_map(
fn($user) => [$user->id],
$users->toArray()
));
Integration with Factories: Combine with Laravel Factories for realistic test data:
$mock->method('handleOrder')
->withConsecutive(...OrderFactory::new()->count(3)->make()->map(
fn($order) => [$order->id]
));
Parallel Testing:
withConsecutive() for parallel test runs.static properties cautiously:
static private $sharedParams = [];
How can I help you explore Laravel packages today?