This library allows overriding fully qualified function calls inside your class methods in order to be able to mock them during testing.
NOTE: The library can be used for other scenarios as well. But we recommend using it for testing purposes only.
- PHP 7.3 or later
- Composer with PSR-4 (PSR-0 is not supported)
$ composer require --dev adriansuter/php-autoload-override 1.5
Usage with PHPUnit
Say we want to unit test the following class Probability
.
namespace My\App;
class Probability
{
public function pick(int $probability, string $color1, string $color2): string
{
if (\rand(1, 100) <= $probability) {
return $color1;
} else {
return $color2;
}
}
}
The class has one method pick
that takes a probability (between 0 and 100) and two color names as arguments.
The method would then use the rand
function of the global scope to generate a random number and
if the generated number is smaller equal to the given probability, then the method would return
the first color, otherwise the method would return the second color.
As we cannot control the output of the rand
function (it is in global scope), we cannot unit test
that method. Well, until now. Using the PHP-Autoload-Override library, it is possible to
override the rand
function and therefore control its generated random number.
After installing the PHP-Autoload-Override library, we would open the bootstrap script of our test suite (see also PHPUnit Configuration). There we will write the following code
// tests/bootstrap.php
/** @var \Composer\Autoload\ClassLoader $classLoader */
$classLoader = require_once __DIR__ . '/../vendor/autoload.php';
\AdrianSuter\Autoload\Override\Override::apply($classLoader, [
\My\App\Probability::class => [
'rand' => function ($min, $max): int {
if (isset($GLOBALS['rand_return'])) {
return $GLOBALS['rand_return'];
}
return \rand($min, $max);
}
]
]);
Now the class Probability
would be loaded into the PHPUnit runtime such that all function calls to the global scoped
rand()
function in the class Probability
get overridden by the closure given above.
Our test class can now be written as follows.
namespace My\App\Tests;
use My\App\Probability;
use PHPUnit\Framework\TestCase;
final class ProbabilityTest extends TestCase
{
protected function tearDown()
{
if (isset($GLOBALS['rand_return'])) {
unset($GLOBALS['rand_return']);
}
}
public function testPick()
{
$p = new Probability();
$GLOBALS['rand_return'] = 35;
$this->assertEquals('blue', $p->pick(34, 'red', 'blue'));
$this->assertEquals('red', $p->pick(35, 'red', 'blue'));
}
}
The test case testPick
would call the pick
method two times. As we have overridden the \rand
function, we can
control its returned value to be always 35. So the first call checks, if the else
-block
gets executed. The second one checks, if the if
-block gets executed. Hooray, 100% code coverage.
Note that this override would only be applied during the unit tests.
The PHP-Autoload-Override library is licensed under the MIT license. See License File for more information.