Unit test that exception is thrown on nth call to function - php

Say you have a method which ultimately boils down to
class Pager
{
private $i;
public function next()
{
if ($this->i >= 3) {
throw new OutOfBoundsException();
}
$this->i++;
}
}
How would you unit test this class. I.e. test whether the exception gets thrown on the third call of next() using PHPUnit? I've added my attempt as an answer, but I'm not sure whether this really is the way to go.

What about testing for null on the first two calls and also test for the exception being thrown like follows:
class PagerTest
{
public function setUp()
{
$this->pager = new Pager();
}
public function testTooManyNextCalls()
{
$this->assertNull($this->pager->next());
$this->assertNull($this->pager->next());
$this->assertNull($this->pager->next());
$this->setExpectedException('OutOfBoundsException');
$this->pager->next();
}
}

It's very important when unit-testing to avoid testing implementation details. Instead, you want to limit yourself to testing only the public interface of your code. Why? Because implementation details change often, but your API should change very rarely. Testing implementation details means that you'll constantly have to rewrite your tests as those implementations change, and you don't want to be stuck doing this.
So what does this mean for the OP's code? Let's look at the public Pager::next method. Code that consumes the Pager class API doesn't care how Pager::next determines if an exception should be thrown. It only cares that Pager::next actually throws an exception if something is wrong.
We don't want to test how the method arrives at it's decision to throw an OutOfBoundsException -- this is an implementation detail. We only want to test that it does so when appropriate.
So to test this scenario we simulate a situation in which the Pager::next will throw. To accomplish this we simply implement what's called a "test seam." ...
<?php
class Pager
{
protected $i;
public function next()
{
if ($this->isValid()) {
$this->i++;
} else {
throw new OutOfBoundsException();
}
}
protected function isValid() {
return $this->i < 3;
}
}
In the above code, the protected Pager::isValid method is our test seam. It exposes a seam in our code (hence the name) that we can latch onto for testing purposes. Using our new test seam and PHPUnit's mocking API, testing that Pager::next throws an exception for invalid values of $i is trivial:
class PagerTest extends PHPUnit_Framework_TestCase
{
/**
* #covers Pager::next
* #expectedException OutOfBoundsException
*/
public function testNextThrowsExceptionOnInvalidIncrementValue() {
$pagerMock = $this->getMock('Pager', array('isValid'));
$pagerMock->expects($this->once())
->method('isValid')
->will($this->returnValue(false));
$pagerMock->next();
}
}
Notice how this test specifically doesn't care how the implementation method Pager::isValid determines that the current increment is invalid. The test simply mocks the method to return false when it's invoked so that we can test that our public Pager::next method throws an exception when it's supposed to do so.
The PHPUnit mocking API is fully covered in Test Doubles section of the PHPUnit manual. The API isn't the most intuitive thing in the history of the world, but with some repeated use it generally makes sense.

Here's what I have at the moment, but I was wondering if there were any better ways of doing this.
class PagerTest
{
public function setUp()
{
$this->pager = new Pager();
}
public function testTooManyNextCalls()
{
for ($i = 0; $i < 10; $i++) {
try {
$this->pager->next();
} catch(OutOfBoundsException $e) {
if ($i == 3) {
return;
} else {
$this->fail('OutOfBoundsException was thrown unexpectedly, on iteration ' . $i);
}
}
if ($i > 3) {
$this->fail('OutOfBoundsException was not thrown when expected');
}
}
}
}

You could use something like:
class PagerTest extends PHPUnit_Framework_TestCase {
/**
* #expectedException OutOfBoundsException
*/
public function testTooManyNextCalls() {
$this->pager = new Pager();
$this->pager->next();
$this->pager->next();
$this->pager->next();
$this->assertTrue(false);
}
}
If an exception is thrown in the 3rd method call, the always-failing assert-statement should never be reached and the test should pass. on the other hand if no exception gets thrown, the test will fail.

You may pass the value $this->i to the exception instantiation, which will then be the message of the exception.
class Pager
{
private $i;
public function next()
{
if ($this->i >= 3) {
throw new OutOfBoundsException($this->i);
}
$this->i++;
}
}
$a=new Pager();
$a->next();
$a->next();
$a->next();
$a->next();
//outputs: "Exception: 3"

Related

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

In __destruct(), how can you see if an exception is currently in flight?

How can I see if an exception is currently in flight, i.e. the stack is unwinding?
In the example below how would you implement isExceptionInFlight()?
<?php
class Destroyer
{
function __destruct() {
if (isExceptionInFlight()) {
echo 'failure';
} else {
echo 'success';
}
}
}
function isExceptionInFlight() {
// ?????
}
function createAndThrow()
{
$var = new Destroyer;
throw new \Exception;
}
createAndThrow();
The purpose of this would be to implement D's scope statement, which is available as a library in multiple other languages. This allows you to get rid of nested try-catch blocks, which in turn makes it easier to do transactions with rollbacks correctly.
Addendum1:
I've looked around in the Zend PHP Engine and executor_globals.exception seems to be what I'm looking for (https://github.com/php/php-src/blob/master/Zend/zend_globals.h). However this value is always nullptr when I inspect it during __destruct(). Any idea where I should look next?
Addendum2:
Inspecting executor_globals.opline_before_exception has led to some progress. However it is not reset to nullptr when the exception has been caught.
Addendum3:
I've found the following code (line 135)
/* Make sure that destructors are protected from previously thrown exceptions.
* For example, if an exception was thrown in a function and when the function's
* local variable destruction results in a destructor being called.
*/
old_exception = NULL;
if (EG(exception)) {
if (EG(exception) == object) {
zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
} else {
old_exception = EG(exception);
EG(exception) = NULL;
}
}
zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) {
if (EG(exception)) {
zend_exception_set_previous(EG(exception), old_exception);
} else {
EG(exception) = old_exception;
}
}
This seems to actively PREVENT me from doing what I want, and explains why executor_globals.exception is always nullptr.
Although I don't recommend, I have implemented it in the past. My approach was (simply put) like this:
Implement custom Exception class
class MyException extends Exception {
public static $exceptionThrown = false;
public function __construct($your parameters) {
self::$exceptionThrown = true;
}
}
Now, every exception should be your own exception implementation instead of default Exception.
class Destroyer {
public function __destruct() {
if(MyException::exceptionThrown() {
Database::rollback();
} else {
Database::commit();
}
}
}

How to check the exception thrown correctly by PHPUnit?

I have div() in Cal class in my CalTest class has following methods to test the div().
public fucnction div($a,$b){
if($b == 0){
throw new Exception("Divided by zero");
}
return $a/$b
}
I can pass only testDiv() but testDiv2().
I want to catch check wheather the exeption thrown correctly using PHPUnit. What am I missing here?? Your help is highly appreciated. Thank you!
Your 2nd screenshot (the one with the error) has
"#expectedException Exception"
while the third has
#expectedException InvalidArgumentException
Do you really still get the error? Did you save the file?
Works for me:
Foo.php
<?php
class Foo
{
static function t()
{
throw new InvalidArgumentException('Hi there');
}
}
?>
FooTest.php
<?php
require_once 'Foo.php';
class FooTest extends PHPUnit_Framework_TestCase
{
/**
* #expectedException InvalidArgumentException
*/
public function testT()
{
Foo::t();
}
}
?>
Result
$ phpunit .
PHPUnit 3.6.10 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 5.25Mb
OK (1 test, 1 assertion)
Just came across the same issue. For some reason PHPUnit will not allow you to set the expectedException to a generic exception and I am not sure why. Personally I opt to throw custom Exception codes rather than have to create a new exception class every time I want to differentiate exceptions.
Here is how I got around it:
/**
* #expectedException Test_Exception
*/
public function testDivByZero()
{
try {
// Fyi you don't need to do an assert test here, as we are only testing the exception, so just make the call
$result = $this->object->div(1,0);
} catch (Exception $e) {
if ('Exception' === get_class($e)) {
throw new Test_Exception($e->getMessage(), $e->getCode());
}
}
}
// Test_Exception.php
class Test_Exception extends Exception
{
public function __construct($message = null, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
This will allow you to engineer your code the way you want, and throw "generic" exceptions. Basically it just tests the Exception class and if it's generic, re-wrap it as a different exception; Test_Exception.
-- Update --
Found out yesterday that they have removed the generic exception restriction in the current "master" branch, which will be 3.7. Apparently the lead engineer has no desire of patching 3.6.
This problem has been fixed in PHPUnit 3.7.x
You can use the following Generic Exception in PHPUnit 3.7.x
/**
* #expectedException Exception
*/
It says so in the exception thrown by PHPUnit:
You must not except the generic exception class.
Make your class throw more detailed Exceptions, and further specify the exception type your expecting in your unit test.
you can do something like this
write your unit test so you inject the values required to trigger the exception then
//assert
$this->assertTrue($this->setExpectedException('PHPUnit_Framework_ExpectationFailedException'));
I create a parent class method to do it. In fact, this class came from the laravel but is valid in any context.
The cool part of this method is using anonymous functions into PHP
<?php
class TestCase
/* if you are not using laravel, you dont need this
extends Illuminate\Foundation\Testing\TestCase */ {
protected function assertThrows( $function , $classException = "/Exception" )
{
try
{
// Anonymous functions FTW
$function();
}
catch( /Exception $objException )
{
// the assertInstanceOf should solve from here but
// it is not working that great
// #see http://stackoverflow.com/questions/16833923/phpunit-assertinstanceof-not-working
if( $objException instanceof $classException ) {
return $this->assertTrue( true );
} else {
// this will throw a error with a cool message
return $this->assertEquals( $classException , get_class( $objException ));
}
}
// no exception happened.
return $this->fail( "Exception " . $classException . " expected." );
}
}
class LanguageTest extends TestCase {
/**
* Test the translation
*
* #return void
*/
public function testTranslation()
{
// this test may be ok
$this->assertThrows(
function(){ throw new Full\Path\Exception(":("); },
"Full\Path\Exception" );
// this test may fail
$this->assertThrows(
function(){ return 1 + 1; },
"Some\Excepted\Exception" );
// this test may work
$this->assertThrows(
function(){ throw new Exception( "sad" ); }
);
}
}

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

Initializing class with bad input makes it unusable but object is still existent

If I want to create a new object that needs certain informations like a product id or something like that but the input is bad how can I elegant manage such a case?
class Product
{
function __construct($id)
{
if(is_invalid_id($id))
{ return false; }
}
}
If I initialize it this way I still get an object (since return inside a constructor doesn't return anything). Another way would be to use exceptions which I then can catch but that's kinda unelegant. 3rd option is to use a static function which then checks the input and then returns the object.
class Product
{
static function init($id)
{
if(is_invalid_id($id))
{ return false; }
return new self($id);
}
private function __construct($id)
{
$this->id = $id;
}
}
$product = Product::init($productId);
The problem here is when I try to extend the class. Either I have to create a init() method for every class I extend (even if it is the exact same code) or return new self() always returns an instance of the parent class.
Throw an exception. Not sure why you consider it unelegant. Man, things were unelegant before exceptions (FALSE, -1, null)
For the problem with self:: you might be able to use late static binding (PHP5.3+):
<?php
class Product
{
static function init($id)
{
if(false)
{ return false; }
$s = get_called_class();
return new $s($id);
}
private function __construct($id)
{
$this->id = $id;
}
function getId()
{
return "Product-$this->id";
}
}
class Headphones extends Product
{
function getId()
{
return "Headphones-$this->id";
}
}
$c1 = Product::init(1);
$c2 = Headphones::init(1);
printf("c1 is %s, c2 is %s\n", $c1->getId(), $c2->getId());
// Prints: c1 is Product-1, c2 is Headphones-1
?>
Your third option is the Factory Pattern.
As you've noticed, the downside is that each class that needs this kind of check generally needs it's own factory method.
I'll give you a non-standard workaround that's universally frowned upon by purists: the hybrid constructor
And it's even more evil than it sounds, because it's actually just a wrapper procedure:
function Product($i) {
$prod = new Product($i);
return $prod->valid() ? $prod : new InvalidProduct();
}
class Product {
function __construct() { ... }
}
class InvalidProduct extends Product implements Stub { }
$new_prod = Product("oops123"); // what will it be?
It simply verifies the object instantantly. If there is something wrong, and now here comes the trick, return a specific stub or NULL object. It might implement a few of the interfaces, but generally cause no side-effects or print an error message once it's inevitable. This concept basically hinges on the viability of carrying a stub object around. It's sometimes more sensible to the application logic to have such a specialized instance than to use decorative test logic.
And then there's the second alternative: just bail with an exception.

Categories