sebastianbergmann/phpunit · error · ExpectationFailedException
%d out of %d expected unordered parameter set%s %s called, i
Error message
%d out of %d expected unordered parameter set%s %s called, index%s [%s] %s not called.
What it means
This failure is raised at verification time (when the test ends or verify() runs) by PHPUnit's unordered parameter set rule: you configured N parameter sets for a mocked method, but only some of them were ever matched by actual invocations. It is the mirror image of running out of sets: here the mock was called too few (or non-matching) times, so leftover unapplied sets remain and PHPUnit reports exactly which configured indexes were never called, failing the test with ExpectationFailedException.
Source
Thrown at src/Framework/MockObject/Runtime/Rule/UnorderedParameterSets.php:110
/**
* Checks if the invocation $invocation matches the current rules. If it
* does the rule will get the invoked() method called which should check
* if an expectation is met.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
if (count($this->applied) !== $this->numberOfConfiguredParameterSets &&
count($this->unapplied) > 0) {
$unappliedIndexes = [];
foreach ($this->unapplied as $parameters) {
$unappliedIndexes[] = $parameters->at();
}
throw new ExpectationFailedException(
sprintf(
'%d out of %d expected unordered parameter set%s %s called, index%s [' . implode(', ', $unappliedIndexes) . '] %s not called.',
count($this->applied),
$this->numberOfConfiguredParameterSets,
$this->numberOfConfiguredParameterSets !== 1 ? 's' : '',
count($this->applied) !== 1 ? 'were' : 'was',
count($unappliedIndexes) !== 1 ? 'es' : '',
count($unappliedIndexes) !== 1 ? 'were' : 'was',
),
);
}
}
}
View on GitHub (pinned to f123cdb2a2)
Solutions
- Make the arguments of the leftover calls actually match: align the configured parameter sets (values, types, constraints) with what the SUT really passes.
- If some sets should legitimately never match, remove them from the expectation — configure only the parameter groups you assert on.
- If the calls should happen but do not, fix the code under test (the missing call is the bug the test is telling you about) or fix the test's arrange step so the SUT reaches that code path.
- If argument drift is hard to pin down, temporarily loosen matching with constraints (equalToCanonicalizing, isType, callback) or assert calls afterwards via a collected-invocations array instead of pre-declared sets.
Example fix
// before
$repo->expects($this->exactly(3))
->method('persist')
->withConsecutiveReplacement(
[$entityA],
[$entityB],
[$entityC], // never called -> verify() failure
);
// after (entityC is persisted only in the batch branch — assert conditionally)
$repo->expects($batch ? $this->exactly(3) : $this->exactly(2))
->method('persist')
->withMatchedByOrder(...$expectedSets); Defensive patterns
Strategy: validation
Validate before calling
// Record actual calls, then assert explicitly instead of relying on leftover sets:
$calls = [];
$repo->method('persist')
->willReturnCallback(function ($e) use (&$calls) { $calls[] = $e; return true; });
// after the SUT runs
self::assertCount($expectedCount, $calls); Try / catch
use PHPUnit\Framework\ExpectationFailedException;
try {
$mock->__phpunit_verify(); // or let the test end
} catch (ExpectationFailedException $e) {
$this->addWarning('Unmet unordered parameter sets: ' . $e->getMessage());
throw $e;
} Prevention
- Keep the list of configured sets and the fixture data in one place so they change together.
- When a branch can skip calls, assert with atLeast()/exactly() counts that match each branch instead of one flat list of sets.
- Run the suite after changing argument types or default values; a set that silently stops matching shows up here.
- Read the reported index(es): they map back to the position in your configured array, telling you exactly which call is missing.
When it happens
Trigger: Configuring more unordered parameter sets than the code under test actually consumes: e.g. ->with() expectations listing sets ['a'], ['b'], ['c'] while the SUT only calls the method twice with arguments matching two of them. verify() detects count($this->applied) !== numberOfConfiguredParameterSets with unapplied sets remaining and throws, naming the unmatched index(es) such as 'index [2] was not called'.
Common situations: An early return or exception in the code under test skips the calls that would have matched the remaining sets; a conditional branch changed after a refactor so one call no longer happens; arguments drifted slightly (type change, added default parameter) so a set no longer matches even though the call count is right; stale expectations left behind when test data was reduced.
Related errors
- Not enough parameter sets configured, only %d parameter sets
- Too many parameter sets given, %d out of %d expected paramet
- Expected invocation at least %d time%s but it occurred %d ti
- Expected invocation at least once but it never occurred.
- Expected invocation at most %d time%s but it occurred %d tim
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/15f209412ec31429.
Report an issue: GitHub.