sebastianbergmann/phpunit · error · NoMoreParameterSetsConfiguredException

Not enough parameter sets configured, only %d parameter sets

Error message

Not enough parameter sets configured, only %d parameter sets given for %s::%s()

What it means

Thrown at call time when a method configured with withParameterSetsInPartialOrder(...) receives more invocations than the total number of parameter sets you configured. PartiallyOrderedParameterSets::apply() increments a counter on every invocation and raises NoMoreParameterSetsConfiguredException as soon as the count exceeds numberOfConfiguredParameterSets, naming the class::method involved. Partial order means unpinned sets may match in any order while 'pinned' sets are tied to a specific call position — but the total number of calls is still capped by the number of sets.

Source

Thrown at src/Framework/MockObject/Runtime/Rule/PartiallyOrderedParameterSets.php:95

        if (count($ordered) > 0 && count($unordered) === 0) {
            $this->ordered = new OrderedParameterSets($ordered);
        } else {
            $this->stack = $ordered;
        }

        if (count($unordered) > 0) {
            $this->unordered = new UnorderedParameterSets($unordered);
        }

        $this->numberOfConfiguredParameterSets = count($stack);
    }

    public function apply(BaseInvocation $invocation): void
    {
        $this->numberOfInvocations++;

        if ($this->numberOfInvocations > $this->numberOfConfiguredParameterSets) {
            throw new NoMoreParameterSetsConfiguredException(
                $invocation,
                $this->numberOfConfiguredParameterSets,
            );
        }

        $stack = $this->stack;

        foreach ($stack as $index => $parameters) {
            if ($parameters->at() === $this->numberOfInvocations - 1) {
                unset($stack[$index]);

                $this->stack     = array_values($stack);
                $this->applied[] = $parameters;

                $parameters->apply($invocation);

                return;
            }

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Use the class::method name in the message plus the stack trace to find the call that ran out of sets.
  2. Add one parameter set per expected invocation, or wrap the remaining calls with a separate matcher if the tail of the interaction is unpredictable.
  3. If the total call count is variable, replace the strict set list with withParameterSetsInAnyOrder() for the free calls plus expects($this->exactly(n)), or capture calls in a willReturnCallback() and assert afterwards.
  4. Shrink or fix the fixture so the number of calls is deterministic and matches the configured sets.
  5. If the excess invocation is duplicated work in production code, fix the caller rather than expanding the expectation.

Example fix

// before
$bus->expects($this->any())->method('dispatch')
    ->withParameterSetsInPartialOrder(['start'], ['end']);
foreach ($jobs as $job) { $bus->dispatch('start'); $bus->dispatch('end'); } // 2n calls, only 2 sets

// after: configure a set for each expected call
$bus->expects($this->exactly(2 * count($jobs)))->method('dispatch')
    ->withParameterSetsInPartialOrder(...$expectedCalls);
Defensive patterns

Strategy: try-catch

Validate before calling

// Make the total deterministic before configuring partial-order sets:
// number of sets must equal number of expected invocations.
$calls = [['start'], ...array_map(fn ($j) => [$j], $jobs), ['end']];
$mock->expects($this->exactly(count($calls)))->method('dispatch')
    ->withParameterSetsInPartialOrder(...$calls);

Try / catch

use PHPUnit\Framework\MockObject\NoMoreParameterSetsConfiguredException;
try {
    $sut->run($jobs);
} catch (NoMoreParameterSetsConfiguredException $e) {
    self::fail('Invocation exceeded configured parameter sets: ' . $e->getMessage());
}

Prevention

When it happens

Trigger: $mock->method('record')->withParameterSetsInOrder(...)->withParameterSetsInPartialOrder(['a'], ['b'], ['c']) while the code calls record() a fourth time; the exception propagates out of the fourth $mock->record(...) call inside the code under test, aborting the test at that point.

Common situations: Loops over fixtures with more items than configured sets; a retry path or repeated event firing adding calls; migrating old withConsecutive() tests and losing a set in translation; growing a data provider without extending the parameter sets; mixing pinned ('pinned' => [...]) and free sets and miscounting the total.

Related errors


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/0d14ac36a6c97640. Report an issue: GitHub.