sebastianbergmann/phpunit · error · PHPUnit\Framework\MockObject\BadMethodCallException

Static method "{method_name}" cannot be invoked on mock obje

Error message

Static method "{method_name}" cannot be invoked on mock object

What it means

PHPUnit 10+ doubles static methods in generated mocks/stubs: the template replaces every static method body with a throw of MockObject\BadMethodCallException('Static method "X" cannot be invoked on mock object'). PHPUnit cannot intercept static calls, so calling a static method through the mock's class (or via static:: inside mocked instance code) always fails at runtime with this message.

Source

Thrown at src/Framework/MockObject/Generator/templates/doubled_static_method.tpl:4

    {modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
    {
        throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object');
    }

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Call the static method on the real class instead of the mock: Foo::staticMethod(), not $mock::staticMethod()
  2. Refactor the code under test so the dependency is an instance method you can mock normally (inject the collaborator)
  3. If the static must run, do not double that class — use the real object, or a hand-written test subclass that overrides the static method
  4. Assert the exception itself when testing that statics are not callable: expectException(BadMethodCallException::class)

Example fix

// before
$logger = $this->createMock(FileLogger::class);
FileLogger::setRetentionPolicy(30); // ok (real class)
$logger::rotate();                   // BadMethodCallException on PHPUnit 10+

// after
$logger = $this->createMock(FileLogger::class);
FileLogger::rotate();                // call statics on the real class
// better: refactor rotate() into an instance method and configure the mock
Defensive patterns

Strategy: type-guard

Validate before calling

$method = new ReflectionMethod($mock, $name);
if ($method->isStatic()) {
    // call it on the real class, not the double
    $realClass = $mock::class;
    // strip the generated Mock_ prefix by calling the original class instead:
    return $originalClass::$name(...$args);
}

Type guard

static function callsStaticOn(string $class, string $method): bool
{
    return (new ReflectionMethod($class, $method))->isStatic();
}

Try / catch

use PHPUnit\Framework\MockObject\BadMethodCallException;

try {
    $mock->{$name}(...$args);
} catch (BadMethodCallException $e) {
    if (str_contains($e->getMessage(), 'cannot be invoked on mock object')) {
        // static on a double: reroute to the real class or refactor the design
    }
    throw $e;
}

Prevention

When it happens

Trigger: Creating $mock = $this->createMock(Foo::class) and then invoking $mock::staticMethod() (or static::staticMethod() / self::staticMethod() inside code under test that runs against the mock class). Any static invocation on the doubled class hits the throwing template body.

Common situations: Upgrading PHPUnit 9 to 10/11/12 where tests previously worked because static methods kept their real implementation; code under test calling static::create() or static::register() factory hooks that now execute against the mock subclass; helpers like Entity::table() invoked on mock entities.

Related errors


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