phpunit - How to mock global function to throw an exception? - php

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.

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%.

Related

How do I use PHPUnit to test __construct with arguments?

I'm new to PHPUnit, and unit testing in general. I can't seem to find a clear tutorial or resource on how best to test:
Passing no argument fails.
How to pass an argument for a constructor test.
Passing an empty argument results in the expected exception.
How would I approach testing this constructor?
<?php
class SiteManagement {
public function __construct (array $config) {
// Make sure we actually passed a config
if (empty($config)) {
throw new \Exception('Configuration not valid', 100);
}
// Sanity check the site list
if (empty($config['siteList'])) {
throw new \Exception('Site list not set', 101);
}
}
}
The example 2.11 of the PHPUnit documentation shows how to test exceptions.
For your specific class, it would be something like this:
$this->expectException(Exception::class);
$object = new SiteManagement([]);
You shouldn't test if the method fails without arguments unless those arguments are optional. This would be out of the scope for a unit test.
A good test battery for your class would be:
/** #test */
public function shouldFailWithEmptyConfig(): void
{
$config = [];
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Configuration not valid');
$this->expectExceptionCode(100);
new SiteManagement($config);
}
/** #test */
public function shouldFailWithoutSiteListConfig(): void
{
$config = ['a config'];
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Site list not set');
$this->expectExceptionCode(101);
new SiteManagement($config);
}
/** #test */
public function shouldMakeAFuncionality(): void
{
$config = [
'siteList' => '',
];
$siteManagement = new SiteManagement($config);
self::assertSame('expected', $siteManagement->functionality());
}

PHPUnit Exception testing with code coverage

I have an unusual problem regarding PhpUnit/unit testing and code coverage.
Background
Background is important in this question. The code is to be tested and 100% coverage is required as part of an audit process. I am aware of points such as "state X could never happen" or "you're not testing this usefully", or other similar points. This is a paper exercise and a project requirement.
Current problems/items
As an example using a php function (random_int)which illustrates the issue:
The function random_int($min, $max) is a core php function.
If this is wrapped (simplified for illustatrative purposes):
class Foo
{
public function Bar(int $min, int $max)
{
return random_int($min, $max)
}
}
Please note that this is not the entire purpose of Foo.. I'm simply removing easy to test things in this example.
This is fine. However, this throws an \Exception and part of the code specification is that we should return exceptions in our own namespace/Exception classes (also including suitably verbose responses etc).
So, if the class FooException is defined in an appropriate namespace, this can be set up as follows:
class Foo
{
public function bar(int $min, int $max)
{
try {
$response = random_int($min, $max);
} catch (Exception $e) {
throw new FooException('Foo::bar() threw an Exception: ' . $e->getMessage());
}
}
}
This effectively ensures that it's almost (but not definitely) impossible to trigger this. (random_int throws an \Exception if it was not possible to gather sufficient entropy... which is unlikely)
Mocking Attempt
In order to remedy this, it's fairly simple to create a call that simply wraps only the function
class Foo
{
public function bar(int $min, int $max)
{
try {
$response = $this->randomInt($min, $max);
} catch (Exception $e) {
throw new FooException('Foo::bar() threw an Exception: ' . $e->getMessage());
}
}
protected function randomInt(int $min, int $max)
{
return random_int($min, $max);
}
}
In the unit test, these can then be mocked using a partial mock:
class FooTest extends TestCase
{
public function testRandomIntegerException()
{
// mock to prove an exception:
$example_class_instance = $this->createPartialMock(Foo::class, ["randomInt"]);
$expected_exception = new FooException();
$example_class_instance
->expects($this->any())
->method("randomInt")
->willThrowException($expected_exception);
// Trigger exception
$this->expectException(GeneratorException::class);
$example_class_instance->randomInteger(1, 5);
}
This works as expected for Unit Tests, and the FooException is correctly generated.
However, this results in the code coverage not counting that test; the unit test is against a mock object, not an instance of the class.
Using namespace overloading
It's possible to use namespace overloading:
// overwrite the base functions in the same namespace as the Generator
namespace FooSpace\;
function random_int($min, $max): int
{
throw new \Exception('Test exception');
}
namespace FooSpaceTests;
class FooTest extends TestCase
{
public function testRandomInteger()
{
$foo = new Foo();
// Trigger exception
$this->expectException(FooException::class);
$foo->randomInteger();
}
}
This also works in terms of a test. It also ensures that the code coverage is correctly set up. However, this function overwrite is permanent as the classes are parsed. This messes other testing up, even if its in a different file/test suite.
I don't think it's possible to "unset" or "undefine" function after these tests.
TL;DR
For a paper/requirements testing suite that should give 100% code coverage
How do you correctly test a class method that includes core php functions?
Thanks in advance

PHPUnit mocked method returns null

I am trying to test the below class using PHPUnit
class stripe extends paymentValidator {
public $apiKey;
public function __construct ($apiKey){
$this->apiKey = $apiKey;
}
public function charge($token) {
try {
return $this->requestStripe($token);
} catch(\Stripe\Error\Card $e) {
echo $e->getMessage();
return false;
}
}
public function requestStripe($token) {
// do something
}
}
My test scripts is like the below:
class paymentvalidatorTest extends PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function test_stripe() {
// Create a stub for the SomeClass class.
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe', 'charge'])
->getMock();
$stripe->expects($this->any())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
}
}
With my test script I was expecting the test double of stripe::charge() method will do exactly as the defined in the original class and the stripe::requestStripe() will return 'Miaw'. Therefore, $stripe->charge('token') should also return 'Miaw'. However, when I run the test I get:
Failed asserting that null matches expected 'Miaw'.
How should I fix this ?
Where you're calling setMethods, you're telling PHPUnit that the mock class should mock the behaviour of those methods:
->setMethods(['requestStripe', 'charge'])
In your case it looks like you want to partially mock the class, so that requestStripe() returns Miaw, but you want charge to run its original code - you should just remove charge from the mocked methods:
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe'])
->getMock();
$stripe->expects($this->once())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
While you're at it you may as well specify how many times you expect requestStripe() to be called - it's an extra assertion with no extra effort, as using $this->any() doesn't provide you with any added benefit. I've included using $this->once() in the example.

Phpunit, how to test if method does "nothing"?

class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
}
}
so its easy to test if it threw exception
/**
* #expectedException Exception
*/
public function test()
{
new Testme(1);
}
but what if it didn't do anything?
public function test()
{
new Testme(2);
?? ? ? ? ?
}
Scenarios
You have two possible scenarios for a function to do nothing:
Scenario 1: No return statement
Your function does nothing because you do not perform actions in it and you do not include the return keyword in it:
public function doNothing()
{
// Do nothing.
}
Scenario 2: With return statement
Your function does nothing because you do not perform actions in it and you do include the return keyword in it without expressing any return value:
public function doNothing()
{
// Do nothing.
return;
}
Other scenarios
I will leave out of the cases to treat the following scenarios:
Case in which you do not return anything but you perform significant actions that can be tested on other objects. In this case you must unit-test the resulting states of the modified objects.
Case in which you do nothing but return something, then you should unit-test the return value.
Exploring the documentation in the PHP manual
For the first case, the PHP manual documents that the evaluated expression of the function will be null. It says here: http://php.net/manual/en/functions.returning-values.php in a note:
If the return is omitted the value NULL will be returned.
For the second case, the PHP manual documents that the evaluated expression of the funcion will also be null. It says here: http://php.net/manual/en/function.return.php in a note:
If no parameter is supplied, then the parentheses must be omitted and NULL will be returned. [...]
Conclusion
It is therefore clearly documented that a function that "does nothing" necessarily evaluates to null.
How to test a function that does nothing
Just assert your expectations:
$this->assertNull( $sut->doNothing() );
This way you "exercise" your function, you run over it making the code-coverage complete all the lines, and you "expect" that "nothing happened" by testing the null value of its evaluation as an expression, as documented.
How to test a constructor that does nothing
Nevertheless to test a constructor... well... common sense: What's the purpose of a constructor? Create an object (instance) of a certain type (class), right?
So... I prefer to start the 100% of my unit tests by checking that the $sut has been created. This is the VERY first test I write when I'm writing the code of a new class. This is the test I write even before the class exists. At the end, this is what the constructor is for. Red bar. Then I create the class. Green bar.
Let's say I have an Email class that takes a string and will be only created if a valid email is passed and throws exception otherwise. this is very similar to your question. A constructor that just "allows the creation" or "denies it by exploding the system".
I usually would do something like this:
//-------------------------------------------------//
// Tests //
//-------------------------------------------------//
/** #dataProvider validEmailProvider **/
public function testCreationIsOfProperClass( string $email )
{
$sut = $this->getSut( $validEmail );
$this->assertInstanceOf( Email::class, $sut );
}
/** #dataProvider invalidEmailProvider **/
public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
{
$this->expectException( EmailException::class );
$this->getSut( $invalidEmail );
}
//-------------------------------------------------//
// Data providers //
//-------------------------------------------------//
public function validEmailProvider() : array
{
return
[
[ 'alice#example.com' ],
[ 'bob.with-several+symbols#subdomain.another.subdomain.example.verylongTLD' ],
]
}
public function invalidEmailProvider() : array
{
return
[
[ 'missing_at_symbol' ],
[ 'charlie#cannotBeOnlyTld' ],
]
}
//-------------------------------------------------//
// Sut creators //
//-------------------------------------------------//
private function getSut( string $email ) : Email
{
return new Email( $email );
}
As I use PHP 7.0 and I put types everywhere, both entering the parameters and also in the return types, if the created object was not an Email, the getSut() function would fail first.
But even if I wrote it omitting the return type, the test tests what it is expected to happen: new Email( 'valid#example.com' ); is itself an expression that shoud evaluate to "something" of class Email::class.
How to test a constructor that does something
Code smell. The constructor probably should not do work. If any, just store parameters. If the constructor "does work" other than storing parameters consider lazy-processing on getters, or delegating that work in a factory or so.
How to test a constructor that "does nothing but store parameters"
Just like before + then get the data.
Test in your first test that the creation is an instance of something.
Then in another different test, exercise something like a getter that gets you what entered in the constructor even if the constructor did not anything (other than storing it).
Hope that this helps.
In PHPUnit 7.2+ you can also use TestCase::expectNotToPerformAssertions()
public function test()
{
// ...
$this->expectNotToPerformAssertions();
}
This has the same behaviour as the #doesNotPerformAssertions annotation.
2018+
Nowadays the best practice is annotation exactly for these cases:
/**
* #doesNotPerformAssertions
*/
public function testSomething()
{
$someService = new SomeObject();
$someService->shallNotFail();
}
Example pull-request
PHPUnit documentation (poor)
It's not possible. Add return statement and assert the result.
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
return true;
}
}
and then
$object = new Testme();
$this->assertTrue($object->testMe(2));
Note: The credits for this solution go to this related answer. The context may seem a little different, but the solution / workaround works the same way. Testing that an exception is not thrown is just the same as testing a method with no return value.
According to this issue thread, there is no built in solution for testing something like DoesNotThrowException in PHPUnit (yet).
So yes, one solution would be to return some dummy value from your method, like
public function testMe ($a)
{
if ($a == 1) { throw new Exception ('YAY'); }
return true;
}
and then assert it in your test. But if you don't want to change the code just for the test, you can work around it:
public function testExceptionIsNotThrown()
{
try {
new Testme(2);
}
catch(Exception $e) {
/* An exception was thrown unexpectedly, so fail the test */
$this->fail();
}
/* No exception was thrown, so just make a dummy assertion to pass the test */
$this->assertTrue(true);
}
It may seem hacky and not very intuitive, but if it's stupid but it works, it's not stupid.
This is an very interesting question, although lot of answers were written, none of them seems to properly answer the question, since you have asked using the class let me explain this way.
Please keep in mind that an instance method you have created in class should have only 2 intentions.
It can alter the state of a class ( change the class properties like private variables )
It returns the state of the class ( getters )
any thing other than this is meaningless unless it is a static method. for example
if you have class like this
class Foo {
private $prop = null;
public function fooMethod() {
$this->prop = "string";
}
public function getProp() {
return $this->prop;
}
}
the method fooMethod() does not return any thing, but it affects the state of $prop property in the class, you can test the method by
$this->assertNotNull( $instance->getProp() );
because you knew if this method is run then the prop $prop should be affected and state of that variable is changed.
Miscellanous Scenario: My method doesn't alter the state and also won't return any state variables.
Then the method is static. It should not be an instance method, and the static methods usually have return type, because they cant affect the state of the class and also can't return state variables. This constraints the static methods from storing a result somewhere (unless you store them globals, don't do that ), so it should definitely return some output. If you don't want to return output, then you could consider returning a boolean from static method.
public function testThrowingException()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('YAY');
(new Testme())->testMe(1);
}
public function testNotThrowingException()
{
$this->expectNotToPerformAssertions();
(new Testme())->testMe(2);
}
I stumled upon the same problem. To ensure "nothing" has happened it's enough to just call you the method in your unit test. If it fails the test will fail anyway.
If you just call your method without the #expectedException annotation like this
public function test()
{
new Testme(1);
}
you'll get an error
There was 1 error:
1) Testme::testMe
Exception: YAY

PHPUnit mocking - fail immediately when method called x times

With PHPUnit, I am testing a sequence of method calls using ->at(), like so:
$mock->expects($this->at(0))->method('execute')->will($this->returnValue('foo'));
$mock->expects($this->at(1))->method('execute')->will($this->returnValue('bar'));
$mock->expects($this->at(2))->method('execute')->will($this->returnValue('baz'));
How can I set up the mock so that, in the above scenario, if execute() is called four or more times, it will immediately fail? I tried this:
$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called too many times.')));
But this also fails if execute() is not called four times. It needs to fail immediately, otherwise the system under test will produce errors of its own, which causes the resulting error message to be unclear.
I managed to find a solution in the end. I used a comination of $this->returnCallback() and passing the PHPUnit matcher to keep track of the invocation count. You can then throw a PHPUnit exception so that you get nice output too:
$matcher = $this->any();
$mock
->expects($matcher)
->method('execute')
->will($this->returnCallback(function() use($matcher) {
switch ($matcher->getInvocationCount())
{
case 0: return 'foo';
case 1: return 'bar';
case 2: return 'baz';
}
throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
}))
;
For special cases like this, I typically use something like the following:
public function myMockCallback() {
++$this -> _myCounter;
if( $this -> _myCounter > 3 ) {
// THROW EXCEPTION OR TRIGGER ERROR
}
... THEN YOUR CASE STATEMENT OR IF/ELSE WITH YOUR CHOICE OF RETURN VALUES
}
... INSIDE TEST FUNCTION ....
$mockObject ->expects($this->any())
->method('myMethod')
->will($this->returnCallback( array ($this, 'myMockCallback' )));
You could separate test to 2 dependent methods, using #depends annotation.
In this case your first test only tests that there are exact 3 method executions, and second - other logic.
What about using data providers?
class MyTest extends PHPUnit.... {
/**
* #var const how much till throwing exception
*/
const MAX_EXECUTE_TILL_EXCEPTION = 3;
public function setUp(){}
public function tearDown(){}
/**
* #dataProvider MyExecuteProvider
*/
pulbic function testMyExecuteReturnFalse($data){
$mock = //setup your mock here
//if using "$ret" doesn't work you cant just call another private helper that will decide if you need to
// return value or throwing exception
if (self::MAX_EXECUTE_TILL_EXCEPTION == $data){
$ret = $this->throwException(new Exception('Called too many times.'));
} else {
$ret = $this->returnValue('foo');
}
$mock->expects($this->at($data))->method('execute')->will($ret);
}
public function MyExecuteProvider(){
return array(
0,1,2,3
)
}
}
This is just another idea, and I think that zerkms suggested very good idea as well

Categories