'phpunit - How to mock global function to throw an exception?
I am writing phpunit tests for my app and one of the units under test uses the following function:
private function createRandomString(): string {
try {
return bin2hex(random_bytes(32));
} catch (Exception) {
$this->logger->warning('Unable to created random binary bytes.');
}
return substr(str_shuffle('0123456789abcdefABCDEF'), 10, 64);
}
The random_bytes function that is included in PHP 7+, may throw an exception, so this method has the "fallback" random string generation in case of problems. I would like to test the problem scenario, so I need the exception to be thrown, but none of the methods I found properly mock this function (I also tried to do the same for bin2hex, but again with no positive result). I have already tried:
- MockBuilder
$builder = new MockBuilder();
$builder->setNamespace(__NAMESPACE__)
->setName('random_bytes')
->setFunction(fn() => throw new Exception());
$mock = $builder->build();
$mock->enable();
- FunctionMock
$mock = $this->getFunctionMock(__NAMESPACE__, 'random_bytes');
$mock->expects($this->once())->willThrowException(new Exception());
- Spy
$spy = new Spy(__NAMESPACE__, 'random_bytes', fn() => throw new Exception());
$spy->enable();
Is there any way of mocking this function? I use pure phpunit and php-mock (not Mockery), because I needed this configuration for other tests.
Solution 1:[1]
Ok, I found some solution/workaround.
I decided to do the Dependency Injection. I created another service that was easy to mock in the typical way.
class RandomizerService {
private const AVAILABLE_CHARS = '0123456789abcdefABCDEF';
/**
* @throws Exception
*/
public function getRandomBytes(int $length): string {
return random_bytes($length);
}
public function getRandomString(int $length): string {
return substr(str_shuffle(self::AVAILABLE_CHARS), 10, 64);
}
}
And used it as follows:
private function createRandomString(): string {
try {
return bin2hex($this->randomizerService->getRandomBytes(32));
} catch (Exception) {
$this->logger->warning('Unable to created random binary bytes.');
}
return $this->randomizerService->getRandomString(64);
}
Then I only mocked it:
$randomizerService->method('getRandomBytes')
->willThrowException(new Exception());
$randomizerService->method('getRandomString')
->willReturn('abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh');
Or if I wanted no mocks, I created it instance:
$randomizerService = new RandomizerService();
And passed the object or mock as a constructor parameter.
Now everything works and it's covered in 100%.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
