sebastianbergmann/phpunit · error · ExpectationFailedException

Parameter count for invocation %s is too low.

Error message

Parameter count for invocation %s is too low.

What it means

PHPUnit throws this when an actual call to the mocked method passed fewer arguments than the number of constraints you gave to ->with(). Parameters::doVerify() compares the invocation's argument list against the configured constraints; if the real call supplies fewer values (typically because trailing parameters are optional and were omitted), verification fails. PHPUnit even appends a hint when the single constraint was anything(): it suspects you wrote ->with($this->anything()) to mean 'any parameters', which actually means 'exactly one parameter of any value'.

Source

Thrown at src/Framework/MockObject/Runtime/Rule/Parameters.php:123

        $invocation           = $this->invocation;
        $invocationParameters = $invocation->parameters();

        if (count($invocationParameters) < count($this->parameters)) {
            $message = 'Parameter count for invocation %s is too low.';

            // The user called `->with($this->anything())`, but may have meant
            // `->withAnyParameters()`.
            //
            // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199
            if (count($this->parameters) === 1 &&
                $this->parameters[0]::class === IsAnything::class) {
                $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead.";
            }

            $this->incrementAssertionCount();

            throw new ExpectationFailedException(
                sprintf($message, $invocation->toString()),
            );
        }

        $parameters = $this->parameters($invocation);

        foreach ($this->parameters as $i => $parameter) {
            $other = null;

            if ($parameter instanceof Callback && $parameter->isVariadic()) {
                $other = $invocationParameters;
            } elseif (isset($invocationParameters[$i])) {
                $other = $invocationParameters[$i];
            }

            $this->incrementAssertionCount();

            $parameter->evaluate(

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. If any number of arguments is acceptable, replace ->with(...) with ->withAnyParameters() (the failure message itself suggests this for the single-anything() case).
  2. Otherwise make the constraints match the real arity: one constraint per argument the code actually passes, using $this->anything() only for arguments you do not care about but that ARE passed.
  3. Check the doubled method's signature for optional/variadic parameters and mirror that shape in with().
  4. If the code under test should pass the argument, fix that call site rather than loosening the expectation.
  5. For variadic matching pass a variadic Callback constraint so one constraint covers the remaining arguments.

Example fix

// before
$logger->expects($this->once())->method('log')->with($this->anything());
$logger->log(); // called with zero arguments

// after: explicitly allow zero or more arguments of any value
$logger->expects($this->once())->method('log')->withAnyParameters();
Defensive patterns

Strategy: validation

Validate before calling

// Before writing with(), check the doubled method's signature and count the
// arguments the SUT actually passes (optional args may be omitted):
$ref = new ReflectionMethod($sut::class, 'log');
$required = $ref->getNumberOfRequiredParameters();
// If the call may pass zero args, do not use with() at all:
// ->withAnyParameters() accepts 0 or more.

Type guard

// Narrow before configuring: does the method accept the arity you are about to constrain?
function acceptsArity(object|string $classOrInstance, string $method, int $arity): bool
{
    $r = new ReflectionMethod($classOrInstance, $method);

    return $arity <= $r->getNumberOfParameters()
        && $arity >= $r->getNumberOfRequiredParameters();
}

Prevention

When it happens

Trigger: ->with($this->anything(), $this->anything()) on a method whose optional second argument the SUT omits; ->with($this->anything()) on a zero-argument call — the message then suggests omitting ->with() or using ->withAnyParameters(); default-valued parameters not passed explicitly by the code under test.

Common situations: Calling $mock->log() with no args while the test expects with('msg'); variadic or optional parameters added/removed in a signature refactor; PHPUnit 9-to-10 migration where with($this->anything()) no longer tolerates a different arity; test doubles for methods with many defaults where production code passes only the first argument.

Related errors


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