sebastianbergmann/phpunit · error · CannotUseOnlyMethodsException
Trying to configure method "%s" with onlyMethods(), but it d
Error message
Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s"
What it means
onlyMethods() deliberately restricts a mock to a subset of methods that must already exist on the targeted class — it cannot invent new ones. For every name passed, PHPUnit reflects the class and checks hasMethod(); the first name with no real method (including inherited ones) triggers CannotUseOnlyMethodsException with the message 'Trying to configure method "%s" with onlyMethods(), but it does not exist in class "%s"'. This separates typos/stale names from legitimate additions and points you to the right API.
Source
Thrown at src/Framework/MockObject/TestDoubleBuilder.php:89
return $this;
}
try {
$reflector = new ReflectionClass($this->type);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
$e->getCode(),
$e,
);
// @codeCoverageIgnoreEnd
}
foreach ($methods as $method) {
if (!$reflector->hasMethod($method)) {
throw new CannotUseOnlyMethodsException($this->type, $method);
}
}
$this->methods = array_merge($this->methods, $methods);
return $this;
}
/**
* Specifies properties that do not declare property hooks for which property hooks should be doubled.
*
* @param list<non-empty-string> $properties
*
* @throws PropertyCannotBeDoubledException
* @throws ReflectionException
*
* @return $this
*/View on GitHub (pinned to f123cdb2a2)
Solutions
- Check the targeted class's actual API (Reflection or IDE) and correct the method name in onlyMethods().
- If you intentionally need a method the class does not declare (e.g. __call-based magic), use addMethods() instead of onlyMethods() — that is its purpose.
- If the method disappeared in an upgraded dependency, update the test to the new API or remove it from the list.
- If the list was copied from another test/class, trim it to methods of the class actually being mocked.
Example fix
// before
$mock = $this->createPartialMock(QueryBuilder::class, ['excecute']); // typo
// after
$mock = $this->createPartialMock(QueryBuilder::class, ['execute']);
// magic method that is not really declared -> use addMethods()
$mock = $this->getMockBuilder(SomeFacade::class)
->addMethods(['magicUndeclaredMethod'])
->getMock(); Defensive patterns
Strategy: validation
Validate before calling
// Check names against reflection before calling onlyMethods():
$methods = ['execute', 'fetchAll'];
$reflector = new ReflectionClass(QueryBuilder::class);
foreach ($methods as $m) {
if (!$reflector->hasMethod($m)) {
self::fail("QueryBuilder has no method '{$m}' — typo, or needs addMethods()?");
}
}
$mock = $this->createPartialMock(QueryBuilder::class, $methods); Type guard
// Typed helper guaranteeing declared methods only:
/** @param list<non-empty-string> $methods */
function onlyDeclaredMethods(string $class, array $methods): array
{
$r = new ReflectionClass($class);
return array_filter($methods, static fn (string $m): bool => $r->hasMethod($m));
} Try / catch
use PHPUnit\Framework\MockObject\CannotUseOnlyMethodsException;
try {
$builder->onlyMethods($names);
} catch (CannotUseOnlyMethodsException $e) {
// $e names the class and the first offending method; fall back to addMethods()
$builder->addMethods(array_diff($names, get_class_methods($this->type)));
} Prevention
- Use ::class + IDE completion for method names; never hand-type them from memory.
- For @method-annotated magic methods, use addMethods() — onlyMethods() is strictly for declared methods.
- After upgrading a dependency, run the mock-heavy suites first to catch removed/renamed methods.
- Keep onlyMethods() lists short and next to the class they target so drift is visible.
When it happens
Trigger: $this->createPartialMock(UserRepository::class, ['findyBy']) (typo), or mocking a method that exists only as @method annotation / __call magic: ReflectionClass::hasMethod('magicName') returns false and CannotUseOnlyMethodsException is thrown from the loop at TestDoubleBuilder.php:87-91. Also triggered after the real method was renamed/removed upstream but the test still lists the old name.
Common situations: Typos or letter-case mismatches (PHP method names are case-insensitive at call time, but hasMethod is case-insensitive too — real culprits are renames, removals, and magic methods); asserting against docblock @method magic methods of a facade; upgrading a dependency whose API dropped/renamed the method; copying an onlyMethods() list between classes; mocking methods of the wrong class because ::class resolved to a parent/interface.
Related errors
- Trying to double property "%s" of class "%s" with doubleProp
- Trying to double property "%s" of class "%s" with doubleProp
- Trying to double property "%s" of class "%s" with doubleProp
- Comparison method %s::%s() does not exist.
- Return value for %s::%s() cannot be generated: %s
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/b78d45e374cb52e6.
Report an issue: GitHub.