Util to allow mocking PHP Internal function calls in tests.
The preferred method of installation is via Composer. Run the following command to install the latest version of a package and add it to your project's composer.json:
composer require-dev idimsh/php-internals-mockerThis mocker is intended to be used in Unit Tests, assume a class like this:
namespace Vendor\Namespace
class MyClass
{
public function openConnction($hostname)
{
return fsockopen($hostname);
}
}Has to be tested with unit tests for method openConnction(). A PhpUnit test case would be like:
namespace VendorTest\Namespace
class MyClassTest extends \PHPUnit\Framework\TestCase
{
public function testOpenConnction(): void
{
$object = new \Vendor\Namespace\MyClass;
$hostname = \uniqid('hostname');
$actual = $object->openConnection($hostname);
// ...
}
}We do not really want to open a connection especially in unit tests, so this mocker can avoid the call to the native PHP fsockopen() and replace it with a call to a defined callback like:
namespace VendorTest\Namespace
use idimsh\PhpInternalsMocker\PhpFunctionSimpleMocker;
class MyClassTest extends \PHPUnit\Framework\TestCase
{
protected function setUp(): void
{
parent::setUp();
PhpFunctionSimpleMocker::reset();
}
public function testOpenConnction(): void
{
$hostname = \uniqid('hostname');
$return = \uniqid('some mock for the return of fsockopen()');
PhpFunctionSimpleMocker::add(
'fsockopen',
\Vendor\Namespace\MyClass::class,
function ($inputHostname) use ($hostname, $return) {
static::assertSame($inputHostname, $hostname);
return $return;
}
);
$object = new \Vendor\Namespace\MyClass;
$actual = $object->openConnection($hostname);
static::assertSame($return, $actual);
/** @noinspection PhpUnhandledExceptionInspection */
PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($this);
}
}1- PhpFunctionSimpleMocker::reset(): should be called in PhpUnit TestCase setUp() method or at the beginning of a test method.
2- PhpFunctionSimpleMocker::add(): to be called after reset() to register the callbacks expected to native functions, signature:
/**
* Register a call back to be called for the PHP internal function which is to be used in the class passed.
*
* If the $callback is null, then this PHP function is not expected to be called.
*
* Assertions can be done inside the callback.
*
* @param string $internalFunctionName The PHP function name to mock
* @param string $beingCalledFromClass The class FQN which calls $internalFunctionName
* @param callable|null $callback
* @param int $numberOfCalls To mock more than once for the same callback, pass the number here
*/
public static function add(
string $internalFunctionName,
string $beingCalledFromClass,
?callable $callback,
int $numberOfCalls = 1
): voidIt can be called multiple times with the same $internalFunctionName and different $callback for each call in the order expected.
The $beingCalledFromClass expects a class FQN which from the namespace will be extracted and the function will be registered at that namespace.
3- PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($testCase): To be called from PhpUnit test method after all the assertions have been registered (last line), this method will make sure that the minimum number of calls has been reached.
4- PhpFunctionSimpleMocker::assertPostConditions(?$testCase): Alternative to PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($testCase) and to be called from PhpUnit TestCase method: assertPostConditions(), instead of calling the previous method at the end of each Test method, a one call passing the TestCase is enough to assert minimum count.
The native PHP function call that is to be mocked and replaced with a callback needs to be (All must apply):
- Called from a class method or a function that is defined inside a namespace and not from a class method or a function which reside in the global namespace.
- The call that PHP native function must not be preceeded by the global namespace resolution operator '\'
- The
use functionstatement is not used to import that native function into the namespace in the class.
Quickly:
- PHP native functions that use references are not supported as of now, put planned to.
- In PhpUnit, assertions for not enough calls has to be explicitly handled by calling
PhpFunctionSimpleMocker::phpUnitAssertNotEnoughCalls($this)orPhpFunctionSimpleMocker::assertPostConditions($this), if any better ideas are there please share. - For any strange issues, the
@runInSeparateProcessoptions of PhpUnit might help, though I did not encounter such cases yet, please report if any.
- Abdulrahman Dimashki
- All Contributors
- An old Symfony class for mocking PHP Internal functions, could not find the source of it. But the code in
PhpFunctionSimpleMocker::register()is taken from it.
There is a solution I havn't tested yet php-mock
Released under MIT License - see the License File for details.