FunctionMock is a simple and elegant way to mock away system/global functions in your tests.
Instalation
composer require --dev avris/function-mock
Usage
Let’s say you have a class that writes something to a file:
<?php
namespace App\Service;
class WorkService
{
public function work(int $input): bool
{
$result = 'correctResult'; // ... calculate the result somehow
$filename = '/tmp/foo';
return file_put_contents($filename, $result);
}
}
To test it, without actually using filesystem, you can use FunctionMock:
<?php
namespace App\Test;
use App\Service\WorkService
use PHPUnit\Framework\TestCase;
class ServiceTest extends TestCase
{
/** @var WorkService */
private $service;
protected function setUp()
{
$this->service = new WorkService();
}
protected function tearDown()
{
FunctionMock::clean();
}
public function testWork()
{
$mock = FunctionMock::create('App', 'file_put_contents', true);
$returnValue = $this->service->work(8);
$this->assertTrue($returnValue);
$this->assertEquals([['/tmp/foo', 'correctResult']], $mock->getInvocations());
}
}
If your tests are in the same namespace with your tested classes, it’s recommended to use __NAMESPACE__
.
The third parameter, the return value of the mocked function, can either be a literal or a callable:
$mock = FunctionMock::create(__NAMESPACE__, 'file_put_contents', function ($filename, $data) {
return $filename === '/tmp/foo';
});
You can disable/re-enable the mock by invoking $mock->disable()
and $mock->enable()
,
as well as clear the list of logged invocations with $mock->clearInvocations()
.
Known limitations
FunctionMock works thanks to the fact that file_get_contents
inside of App\Service
namespace references to the function
App\Service\file_get_contents
and only if it’s not defined it falls back to the global \file_get_contents
.
FunctionMock defines this App\Service\file_get_contents
function on the fly
and makes it adjustable to your needs in the runtime.
- If you already have invoked
file_get_contents
inside ofApp\Service
before registering a mock, PHP will continue using the global function and it cannot be overwritten anymore. - If you’re using
\file_get_contents
(explicitly global namespace) inside your tested class, there’s obviously no way to mock it. - After being registered, mock function cannot be really unset, only disabled.