sebastianbergmann/phpunit · error · ExpectationFailedException

Method was expected to be called %d time%s, actually called

Error message

Method was expected to be called %d time%s, actually called %d time%s.

What it means

PHPUnit throws this at mock-verification time when a method stubbed with expects($this->exactly(N)) (or once(), twice(), times(N), never()) was called a different number of times than expected. InvokedCount::verify() runs after the test body and requires the recorded count to equal the expected count exactly — both too few and too many calls fail here (if the test survived that long; see the invokedDo error for the mid-test variant).

Source

Thrown at src/Framework/MockObject/Runtime/Rule/InvokedCount.php:63

    }

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

    /**
     * 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
    {
        $actualCount = $this->numberOfInvocations();

        if ($actualCount !== $this->expectedCount) {
            throw new ExpectationFailedException(
                sprintf(
                    'Method was expected to be called %d time%s, actually called %d time%s.',
                    $this->expectedCount,
                    $this->expectedCount !== 1 ? 's' : '',
                    $actualCount,
                    $actualCount !== 1 ? 's' : '',
                ),
            );
        }
    }

    /**
     * @throws ExpectationFailedException
     */
    protected function invokedDo(BaseInvocation $invocation): void
    {
        $count = $this->numberOfInvocations();

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Read the expected vs actually-called counts in the message and decide which one reflects the real contract.
  2. If the actual count is correct, update the expectation: exactly(3) instead of exactly(2), or switch to atLeast()/atMost() when the exact count is not part of the contract.
  3. If the expected count is correct, debug why the code under test calls the method a different number of times (extra loop iteration, missing return, double dispatch).
  4. For zero-times assertions use never() — but note that even one invocation fails verification with this message context.
  5. When you only care that the method ran, prefer expects($this->atLeastOnce()) to make the test less brittle.

Example fix

// before
$cache->expects($this->exactly(2))->method('get');
$service->lookup($id); // first call warms the cache, so get() runs only once

// after: assert the count the code really produces, or loosen it
$cache->expects($this->atLeast(1))->method('get');
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: $mock->expects($this->exactly(2))->method('log') while the code calls log() once or three times; once() combined with code that calls the method zero or two times; never() where the method ends up being called at least once.

Common situations: Conditional logic (caching, feature flags, short-circuit evaluation) changing how many times a collaborator is hit; loops whose iteration count depends on fixtures; refactors that hoist or duplicate a call; once() used as a lazy 'it gets called' assertion that breaks when a second legitimate call is added; concurrent/recursive code paths multiplying invocations.

Related errors


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