sebastianbergmann/phpunit · error · ExpectationFailedException
No entry in the value map matched the invocation of %s::%s()
Error message
No entry in the value map matched the invocation of %s::%s() with parameters (%s)
What it means
ReturnValueMap is the stub behind returnValueMap() (non-strict) and willReturnMap() (strict). On each invocation it looks for a map row whose parameter segment matches the invocation's arguments (same arity, strict === comparison or Constraint evaluation); the last element of the row is the return value. In strict mode (willReturnMap), when no row matches, PHPUnit throws ExpectationFailedException instead of silently returning null, so an unmatched call cannot hide as a null that later fails somewhere else.
Source
Thrown at src/Framework/MockObject/Runtime/Stub/ReturnValueMap.php:64
*/
public function invoke(Invocation $invocation): mixed
{
$parameterCount = count($invocation->parameters());
foreach ($this->valueMap as $map) {
if (!is_array($map) || $parameterCount !== (count($map) - 1)) {
continue;
}
$return = array_pop($map);
if ($this->parametersMatch($map, $invocation->parameters())) {
return $return;
}
}
if ($this->strict) {
throw new ExpectationFailedException(
sprintf(
'No entry in the value map matched the invocation of %s::%s() with parameters (%s)',
$invocation->className(),
$invocation->methodName(),
Exporter::shortenedExport($invocation->parameters()),
),
);
}
return null;
}
/**
* @param array<mixed> $mapParameters
* @param array<mixed> $invocationParameters
*/
private function parametersMatch(array $mapParameters, array $invocationParameters): bool
{View on GitHub (pinned to f123cdb2a2)
Solutions
- Add a map row for the actually-passed parameters (copy them from the exception message, which prints the exact invocation parameters via Exporter::shortenedExport).
- Fix arity: every row must have exactly one more element than the method's argument count for that call site, and default arguments must be spelled out in the row.
- Use constraints instead of literals in rows (equalTo with loose settings, isType, callback, instanceof) when exact identity cannot be guaranteed.
- If unmatched calls returning null is acceptable, use the non-strict returnValueMap() instead of willReturnMap(); better still, switch to willReturnCallback() for full control over matching and defaults.
Example fix
// before
$cache->method('get')
->willReturnMap([['user-1', $user], ['user-2', $user2]]);
$cache->get('user-3'); // strict mode -> ExpectationFailedException
// after
$cache->method('get')
->willReturnMap([
['user-1', $user],
['user-2', $user2],
['user-3', null], // explicit fallback row
]); Defensive patterns
Strategy: validation
Validate before calling
// Validate map rows against the method signature before wiring the stub:
$map = [
['user-1', $u1],
['user-2', $u2],
['user-3', null], // default row for any key
];
$paramCount = (new ReflectionMethod(Cache::class, 'get'))->getNumberOfRequiredParameters();
foreach ($map as $row) {
assert(count($row) === $paramCount + 1, 'each row = params + return value');
} Type guard
// Narrow a value before putting it into a map row when strict identity matters:
function isCacheableKey(mixed $key): bool
{
return is_string($key) && preg_match('/^[a-z0-9-]+$/', $key) === 1;
} Try / catch
use PHPUnit\Framework\ExpectationFailedException;
try {
$sut->lookup($key);
} catch (ExpectationFailedException $e) {
if (str_contains($e->getMessage(), 'No entry in the value map matched')) {
self::fail('Add a willReturnMap row for parameters: ' . $e->getMessage());
}
throw $e;
} Prevention
- Every row: [param1, ..., paramN, returnValue] — the return value is always last and arity must be params+1.
- Watch strict comparison: 0 !== '0', null !== false; export the actual parameters from the message and diff them.
- Use PHPUnit constraints inside rows for fuzzy matches.
- Choose returnValueMap() (non-strict, returns null) only when a null default is acceptable; keep willReturnMap() strict otherwise.
When it happens
Trigger: $mock->method('get')->willReturnMap([['a', 1], ['b', 2]]) followed by $mock->get('c'): no row's parameter segment equals ['c'], so with strict=true the 'No entry in the value map matched ... with parameters (...)' exception is thrown at call time inside the test. Non-matching rows are silently skipped when their arity differs (count($map) - 1 !== parameterCount) or their values differ under strict comparison.
Common situations: A default-argument call omits a parameter the map rows include (or vice versa), so arity check rejects every row; values that look equal but differ in type (0 vs '0', null vs '', false vs 0) under the strict !== comparison; passing objects that are equal-but-not-identical instances; forgetting that the return value must be the LAST element of each row; enums/named-argument reordering changes what the SUT actually passes.
Related errors
- Only %d return values have been configured for %s::%s()
- Not enough parameter sets configured, only %d parameter sets
- %d out of %d expected unordered parameter set%s %s called, i
- Trying to configure method "%s" with onlyMethods(), but it d
- Trying to double property "%s" of class "%s" with doubleProp
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/de531002e1cc1e67.
Report an issue: GitHub.