Let's say you have the following:
class ComposerFileService {
public function fetchComposerJsonObject(string $composerJsonFilePath): ?object
{
return json_decode(file_get_contents($composerJsonFilePath));
}
}
To test this method in PHPUnit, use php-mock/php-mock-phpunit
library. First, install it with:
composer require --dev php-mock/php-mock-phpunit
The second step is to use \phpmock\phpunit\PHPMock
trait in your PHPUnit test class. We can then write a unit test:
/**
* @dataProvider fetchComposerJsonObject_HappyFlow
*
* @param string $composerJsonFilePath
* @param string $fileGetContentsMockValue
* @param object $jsonDecodeMockValue
*/
public function testFetchComposerJsonObject_HappyFlow(
string $composerJsonFilePath,
string $fileGetContentsMockValue,
object $jsonDecodeMockValue
) {
$fileGetContentsMock = $this->getFunctionMock(dirname(ComposerFileService::class), 'file_get_contents');
$fileGetContentsMock->expects($this->once())
->with($composerJsonFilePath)
->willReturn($fileGetContentsMockValue);
$jsonDecodeMock = $this->getFunctionMock(dirname(ComposerFileService::class), 'json_decode');
$jsonDecodeMock->expects($this->once())
->with($fileGetContentsMockValue)
->willReturn($jsonDecodeMockValue);
$service = $this->getMockBuilder(ComposerFileService::class)
->disableOriginalConstructor()
->onlyMethods([])
->getMock();
$result = $service->fetchComposerJsonObject($composerJsonFilePath);
$this->assertEquals($jsonDecodeMockValue, $result);
}
Data provider for this test is simply:
private function fetchComposerJsonObject_HappyFlow(): array {
$faker = Factory::create();
return [
'happy flow' => [
'composerJsonFilePath' => $faker->unique()->word(),
'fileGetContentsMockValue' => $faker->unique()->word(),
'jsonDecodeMockValue' => (object)[ 'key' => $faker->unique()->word()]
]
];
}
In order to properly test this method, we did the following:
-
$faker
in the data provider is simply using https://fakerphp.github.io to generate random data for our tests. It's not that relevant for our example. - We are not using a real file from the filesystem. Unit tests should not do that.
-
file_get_contents
andjson_decode
(PHP's default) functions are not called directly, but mocked. - We are testing that
file_get_contents
is getting called once, with the value of$composerJsonFilePath
. The return value is randomly generated (by faker), and is irrelevant in the context of testing thefile_get_contents
function. We'll just need it in order to properly testjson_decode
. - We are testing that
json_decode
is getting called once, with the value of whatfile_get_contents
returned. - And finally, we are testing that our method returned the result of calling
json_decode
.
For more information and examples of mocking and spying on PHP's default functions, take a look at the following libraries:
- https://github.com/php-mock/php-mock - a testing library which mocks non deterministic built-in PHP functions;
- https://github.com/php-mock/php-mock-phpunit - integrates the function mock library PHP-Mock with PHPUnit;
Top comments (0)