sebastianbergmann/phpunit · error · ExpectationFailedException

Expected invocation at least once but it never occurred.

Error message

Expected invocation at least once but it never occurred.

What it means

PHPUnit throws this when a method configured with expects($this->atLeastOnce()) was never called at all during the test. InvokedAtLeastOnce::verify() runs when the mock is verified after the test body: it reads the recorded invocation count and fails because the count is zero. In short: your test promised the mocked method would be hit at least one time, and the code under test never reached that call.

Source

Thrown at src/Framework/MockObject/Runtime/Rule/InvokedAtLeastOnce.php:38

final class InvokedAtLeastOnce extends InvocationOrder
{
    public function toString(): string
    {
        return 'invoked at least once';
    }

    /**
     * Verifies that the current expectation is valid. If everything is OK the
     * code should just return, if not it must throw an exception.
     *
     * @throws ExpectationFailedException
     */
    public function verify(): void
    {
        $count = $this->numberOfInvocations();

        if ($count < 1) {
            throw new ExpectationFailedException(
                'Expected invocation at least once but it never occurred.',
            );
        }
    }

    public function matches(BaseInvocation $invocation): bool
    {
        return true;
    }
}

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Debug or trace the code under test to confirm which branch runs — usually an early return, failed precondition, or exception prevents the call.
  2. Verify the mock is actually injected into the object under test (constructor/setter argument), not created and left unused.
  3. Check the method name string passed to method() matches the collaborator's real method exactly.
  4. If the call legitimately does not happen on this path, remove the atLeastOnce() expectation or move it to a test that exercises the path where the call occurs.
  5. Prefer asserting the end state (returned value, persisted data) over strict interaction expectations when the interaction is incidental.

Example fix

// before
$repo->expects($this->atLeastOnce())->method('save');
$service->process($invalidDto); // validation fails, save() never called

// after: exercise the path that actually saves
$repo->expects($this->atLeastOnce())->method('save');
$service->process($validDto);
Defensive patterns

Strategy: validation

Validate before calling

// Cheap pre-check: make sure the SUT actually reaches the interaction by asserting
// a state change or return value the call produces before relying on atLeastOnce().
$this->assertNotNull($service->process($validDto)); // path that must call the collaborator

Prevention

When it happens

Trigger: $mock->expects($this->atLeastOnce())->method('save') followed by running code that never reaches save() — e.g. input validation fails first, the method name is misspelled in method('svae'), or the mocked dependency is not the instance the code under test actually uses.

Common situations: Guard clauses or exceptions in the SUT short-circuiting before the expected call; wiring a fresh mock via createMock() but injecting a different (real) object into the constructor; typos in method() names; tests that exercise an error path but still assert the happy-path interaction; refactoring that renamed the called method on the collaborator.

Related errors


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