sebastianbergmann/phpunit · error · ComparisonMethodDoesNotDeclareExactlyOneParameterException

Comparison method %s::%s() does not declare exactly one para

Error message

Comparison method %s::%s() does not declare exactly one parameter.

What it means

The comparison method used by assertObjectEquals() must declare exactly one parameter, and that parameter must be required (no default). PHPUnit reflects the method and throws this exception when getNumberOfParameters() or getNumberOfRequiredParameters() differs from 1, because the constraint calls it as `$other->equals($expected)` with exactly one argument.

Source

Thrown at src/Framework/Constraint/Object/ObjectEquals.php:114

            );
        }

        if ($returnType->allowsNull()) {
            throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
                $other::class,
                $this->method,
            );
        }

        if ($returnType->getName() !== 'bool') {
            throw new ComparisonMethodDoesNotDeclareBoolReturnTypeException(
                $other::class,
                $this->method,
            );
        }

        if ($method->getNumberOfParameters() !== 1 || $method->getNumberOfRequiredParameters() !== 1) {
            throw new ComparisonMethodDoesNotDeclareExactlyOneParameterException(
                $other::class,
                $this->method,
            );
        }

        assert(count($method->getParameters()) > 0);
        $parameter = $method->getParameters()[0];

        if (!$parameter->hasType()) {
            throw new ComparisonMethodDoesNotDeclareParameterTypeException(
                $other::class,
                $this->method,
            );
        }

        $type = $parameter->getType();

        if (!$type instanceof ReflectionNamedType) {

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Declare exactly one required typed parameter: `public function equals(self $other): bool`
  2. Move extra options into the method's behavior or separate methods (equalsStrict()) and pass that name to assertObjectEquals()
  3. If a no-arg comparator compares against an internal expected value, restructure the test to use assertThat($actual->value(), $this->equalTo($expectedValue))

Example fix

// before
class Money
{
    public function equals(self $other = null, bool $strict = true): bool { ... }
}
$this->assertObjectEquals($expected, $actual);

// after
class Money
{
    public function equals(self $other): bool { ... }
}
$this->assertObjectEquals($expected, $actual);
Defensive patterns

Strategy: type-guard

Type guard

function comparisonMethodTakesExactlyOneRequiredParameter(object $actual, string $method = 'equals'): bool
{
    $m = (new ReflectionObject($actual))->getMethod($method);
    return $m->getNumberOfParameters() === 1
        && $m->getNumberOfRequiredParameters() === 1;
}

Prevention

When it happens

Trigger: equals() declared with zero parameters (`public function equals(): bool`), with two or more parameters, or with one optional parameter having a default value (`public function equals(self $other = null): bool`); also variadic signatures `equals(self ...$others)`.

Common situations: Java-ported equals(mixed $other = null) with a nullable default; 'fluent' comparison APIs like equals($other, $strict = true); stateless helpers that compare against an injected field and take no argument.

Related errors


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