idimsh / php-internals-mocker
Helps mocking PHP internal functions calls
Requires
- php: ~7.1
Requires (Dev)
- phpunit/phpunit: >=7.0 <8
- squizlabs/php_codesniffer: ^3.0
This package is auto-updated.
Last update: 2024-11-22 06:46:05 UTC
README
Util to allow mocking PHP Internal function calls in tests.
Installation
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-mocker
Usage
This 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); } }
Methods Manual
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 ): void
It 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.
Usage Conditions
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 function
statement is not used to import that native function into the namespace in the class.
Limitations
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
@runInSeparateProcess
options of PhpUnit might help, though I did not encounter such cases yet, please report if any.
Credits
- 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.
Alternatives
There is a solution I havn't tested yet php-mock
License
Released under MIT License - see the License File for details.