I want to test (with PHPUnit) a method that contains a foreach loop. I want the full patch coverage. The original code is little too complex, so I created a minimal example below to illustrate my problem. There are 3 cases for the foreach
loop in the PHP. (I use the PHP 8.2.)
- Empty iterable something.
- Not empty iterable something.
- Not iterable something.
public function dodo(array $a)
{
foreach ($a as $one) {
return $one;
}
return null;
}
It is easy to cover first two: a not empty array and an empty array. But how can I pass a not iterable something as the function argument? I have tried few ways, but I’ve got the TypeError.
$something->dodo(null); # TypeError
call_user_func([$something, 'dodo'], null); # TypeError
$rClass = new ReflectionClass($something);
$rMethod = $rClass->getMethod('dodo');
$rMethod->invoke($something, null); # TypeError
I don’t want to remove or change the type from the method definition. This would make the code a little less readable. Is there a way around? How can I write a test that will cover all cases of the foreach loop?
In other words:
How i can call the dodo
with an argument of a wrong type? I want to write test with very high code paths coverage.
2
Answers
When you add
array
as a type declaration, you basically tell PHP to make sure that no other type is allowed to be passed to your method. And the way PHP makes sure that no other type is passed, is by throwing thatTypeError
you are seeing.In a way your code is equivalent to the following:
You basically answered your own question. You can call the method with a non-array argument just like you did:
$something->dodo(null);
. TheTypeError
you are seeing is the expected result.And since PHP is already taking care that you are only working with arrays here, you have in fact covered all cases of that
foreach
loop.This is incorrect. These are not your paths. I’m not sure where you got the idea that "Path 3" has to do with passing a non-iterable to a typehinted iterable. The three paths that XDebug is reporting are:
This is tested by calling
dodo()
with a non-empty iterable.This is tested by calling
dodo()
with an empty iterable.This can not be covered by a test because your loop never exits. So, you don’t actually have three paths. However, XDebug doesn’t know that. To avoid this odd issue, I’d simply rewrite the method so that it is not indeterminate: