sebastianbergmann/phpunit · error · ExpectationFailedException

Expectation for %s failed. %s

Error message

Expectation for %s failed.
%s

What it means

Wrapped ExpectationFailedException from Matcher::invoked(): when a real call arrives and the matcher's parametersRule->apply($invocation) fails (the actual arguments do not satisfy with() constraints), PHPUnit rethrows with 'Expectation for <method> failed.' prefixed to the original parameter mismatch message, preserving the comparison failure. It means the method was called, but with different arguments than the expectation asserts.

Source

Thrown at src/Framework/MockObject/Runtime/Matcher.php:139

            throw new MethodNameNotConfiguredException;
        }

        if ($this->afterMatchBuilderId !== null) {
            $matcher = $invocation->object()
                ->__phpunit_getInvocationHandler()
                ->lookupMatcher($this->afterMatchBuilderId);

            if ($matcher === null) {
                throw new MatchBuilderNotFoundException($this->afterMatchBuilderId);
            }
        }

        $this->invocationRule->invoked($invocation);

        try {
            $this->parametersRule?->apply($invocation);
        } catch (ExpectationFailedException $e) {
            throw new ExpectationFailedException(
                sprintf(
                    "Expectation for %s failed.\n%s",
                    $this->methodNameRule->failureDescription($this->className),
                    $e->getMessage(),
                ),
                $e->getComparisonFailure(),
            );
        }

        if ($this->stub !== null) {
            return $this->stub->invoke($invocation);
        }

        return $invocation->generateReturnValue();
    }

    /**
     * @throws ExpectationFailedException

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Read the nested message: it shows the expected vs actual argument values — align the expectation with the real arguments (or fix the production code that passes wrong ones).
  2. For object arguments, either implement value equality on the DTO or use a callback constraint: ->with($this->callback(fn ($m) => $m->amount === 100)).
  3. Use $this->identicalTo($x) when identity is the real requirement, and $this->equalTo($x) (default) for structural equality.
  4. If argument order varies, withParameterSetsInAnyOrder([...]) replaces several brittle with() rules.

Example fix

// before
$mailer = $this->createMock(Mailer::class);
$mailer->expects($this->once())
    ->method('send')
    ->with('Hello') // code actually sends 'Hello!' 
    ->willReturn(true);

// after
$mailer->expects($this->once())
    ->method('send')
    ->with('Hello!')
    ->willReturn(true);
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check the exact constraint against the value you expect the subject to pass
$expected = 'Hello!';
$actual = $subject->buildGreeting();
$this->assertSame($expected, $actual); // fail early with a clean diff before the mock call

Type guard

// constraints double as guards: verify a value satisfies the with() rule before the call
$constraint = $this->identicalTo($token);
if (!$constraint->evaluate($candidate, '', true)) {
    throw new RuntimeException('subject will pass a different token');
}

Try / catch

try {
    $subject->run($mock);
} catch (PHPUnit\Framework\ExpectationFailedException $e) {
    // assertion failure during the call: inspect getComparisonFailure() diff,
    // fix the with() constraints to match reality, then remove the catch
}

Prevention

When it happens

Trigger: Expectation ->method('transfer')->with(100, 'EUR') but the code under test calls transfer(50, 'EUR'); identical() vs equalTo() semantics (with() defaults to equal, not same) so same-looking objects differ; float/string coercion mismatches under strict comparison inside the parameter rule.

Common situations: The subject transforms arguments before delegating to the collaborator (multiplied amounts, trimmed strings, wrapped DTOs); expectations copied from a spec where values were different; with(new Money(100)) comparing object identity instead of equality; timezone/locale formatting changing string arguments.

Related errors


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