In order to create a mock version of a global function for unit testing, I define its mock version in the namespace of the SUT class, as is explained here
Consider a case of global function foo()
which is used in namespace Bar
and in namespace Baz
. If I want to use the same mock version of foo()
in both places, it seems I am forced to make two declarations of the mock for foo()
/* bar-namespace-mocks.php */
namespace Bar;
function foo(){
return 'foo mock';
}
/* baz-namespace-mocks.php */
namespace Baz;
function foo(){
return 'foo mock';
}
This code does not conform to the DRY principal.
It would be preferred to have one declaration of the foo mock
/* foo-mock.php */
function foo(){
return 'foo mock';
}
and then import into each namespace as needed like the following pseudo code:
/* namespace-mocks.php */
namespace Bar{
import 'foo-mock.php';
}
namespace Baz{
import 'foo-mock.php';
}
Importing using include, e.g.
namespace Baz;
include 'foo-mock.php'
does not cause the mock to be declared in the Baz
namespace. Is there any way to declare a function in more than one namespace without having more than one version of it?
2
Answers
If you need to abstract away a native function, then make a contract for it, so that you can use dependency injection for the service it provides:
Then provide a default implementation that uses the native function:
Then your main class might look something like:
So, you’ve got an optional argument in the constructor, and if you don’t provide a value, you’ll get the native foo function as a default. You’d just do:
But if you want to override that, you can do:
Then, in your test, you can build an explicit mock:
And pass in instance of that:
Or use the PHPUnit mock builder:
If you want a simpler version, you could just use a callable:
While I whole-heartedly agree with Alex Howansky’s answer, if you’re truly hell-bent on using plain functions, then you can always:
Output:
Though at this point we’re really just on our way to reinventing classes, but worse. At some point you’re going to wonder how to namespace a variable, and that is simply not possible, so you’ll be rolling this into classes at that point.
Classes, interfaces, and traits/inheritance would be most properly leveraged to solve this problem.