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
- Call the static method on the real class instead of the mock: Foo::staticMethod(), not $mock::staticMethod()
- Refactor the code under test so the dependency is an instance method you can mock normally (inject the collaborator)
- 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
- 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
- Treat 'never mock statics' as a review rule: static factories and registries belong on real classes in tests
- Prefer constructor/method injection of collaborators so mocks never need static behavior
- When upgrading PHPUnit 9 to 10+, grep tests for `::` calls on mock variables and static:: usages in tested code
- Hand-write a test subclass overriding statics if a static seam is unavoidable
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
- Not enough parameter sets configured, only %d parameter sets
- %d out of %d expected unordered parameter set%s %s called, i
- Only %d return values have been configured for %s::%s()
- No entry in the value map matched the invocation of %s::%s()
- Trying to configure method "%s" with onlyMethods(), but it d
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/122b093a79c6b57a.
Report an issue: GitHub.