How to check the exception thrown correctly by PHPUnit? - php

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" ); }
);
}
}

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

PHPUnit: how test the __() php

I have this exception:
throw new \DomainException(__("message d'erreur"));
I don't know how can I do the test of this instruction by PHPUnit: __().
Im sure there are other ways which i'm not aware of but I would do it like the following, if you downvote please comment why. If I learn something new I'm happy.
Ok, depending on whether the application expects the function to be defined to work or whether its expected but not part of the current test you could mock it:
Using: php-mock/php-mock-phpunit
<?php
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase
{
use \phpmock\phpunit\PHPMock;
/**
* __() function must exist or test fails.
*/
public function testDoubleUndescoreFunction()
{
$this->assertTrue(function_exists('__'), '__() function does not exist');
}
// or
/**
* __() function will be mocked.
*/
public function testDoubleUndescoreFunction()
{
// mock __() function
if (!function_exists('__')) {
$mocks['__'] = $this->getFunctionMock(__NAMESPACE__, "__");
$mocks['__']->expects($this->any())->willReturnCallback(
function ($value) {
$this->assertInternalType('string', $value);
return $value;
}
);
}
// test
$this->assertTrue(function_exists('__'), '__() function does not exist');
try {
//
// run test which will cause the exception
} catch (\Exception $e) {
$this->assertInstanceOf('DomainException', $e);
$this->assertEquals('message d\'erreur', $e->getMessage());
}
}
}
?>

Testing PHPUnit to test a "__construct() must be an instance of .." does not recognize exception

I have the following code to test if the class constructor will fire the exception but PHPUnit test fail on it. I am trying to figure out what I am doing wrong.
/** #test */
public function should_require_instance_of_uuid()
{
$this->setExpectedException('Exception');
$id = new BusinessPartnerId;
}
PHPunit gives the following error:
There was 1 error:
1) Tests\Domain\Model\Common\BusinessPartner\BusinessPartnerIdTest::should_require_instance_of_uuid
Argument 1 passed to Domain\Model\Common\BusinessPartner\BusinessPartnerId::__construct() must be an instance of Rhumsaa\Uuid\Uuid, none given, called in tests/Comain/Model/Common/BusinessPartner/BusinesPartnerIdTest.php on line 14 and defined
Domain/Model/Common/BusinessPartner/BusinessPartnerId.php:20
tests/Domain/Model/Common/BusinessPartner/BusinesPartnerIdTest.php:14
I am not sure why this test is not passing? I have also tried:
$this->setExpectedException('InvalidArgumentException');
You test should looks:
If you have class:
class Stack
{
public function __construct(\Model $model)
{
}
}
Then test:
/**
* #test
*/
public function shouldCheckInstance()
{
try {
new Stack(null);
} catch(\Exception $e) {
$this->assertContains('must be an instance of Model', $e->getMessage());
}
}
While I have not worked with the current PHPUnit, the older versions did not allow you to capture the basic Exception, but would capture your own extension to the Exception class. Also, the exception is likely namespaced, so it is \Exception, not just Exception.
I also used the #expectedException in the doc block to indicate what exception I was expecting.
/**
* #test
* #expectedException \MyNamespace\MyException
*/
public function shouldCheckInstance()
{
new MyClass():
}
or
/**
* #test
*/
public function shouldCheckInstance()
{
$this->setExpectedException('\MyNamespace\MyException');
new MyClass():
}

Unit test that exception is thrown on nth call to function

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"

How to execute code after trigger_error(..., E_USER_WARNING) in unit test (PHPUnit)?

I have code like this:
class ToBeTested
{
function simpleMethod($param)
{
if(0 === $param)
{
trigger_error("Param is 0!", E_USER_WARNING);
return false;
}
return true;
}
}
and test for this code:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
$toBeTestedObject = new ToBeTested();
$this->assertFalse($toBeTestedObject->simpleMethod(0));
}
}
I know how to test, if the error is triggered ($this->setExpectedException()), but I don't know how to execute the code after trigger_error() function.
Remember that in PHPUnit E_USER_WARNING is not converted into PHPUnit_Framework_Error_Warning (which can be disabled), but it is converted into PHPUnit_Framework_Error (which can't be disabled).
This is one of those places where you are 'officially' allowed to use the # operator :)
Make one test to check the return value, another test to check if the warning gets triggered. And by the way, I'd suggest you do test if the warning is triggered.
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethodReturnValue()
{
$toBeTestedObject = new ToBeTested();
$this->assertFalse(#$toBeTestedObject->simpleMethod(0));
}
/**
* #expectedException PHPUnit_Framework_Error
*/
function testSimpleMethodEmitsWarning() {
$toBeTestedObject = new ToBeTested();
$toBeTestedObject->simpleMethod(0);
}
}
What you should be using is set_error_handler() (link) and restore_error_handler() which lets you set a function to deal with errors of a given type. It also has the added bonus of giving you a place to test the warning at the same time.
So, something like this:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
set_error_handler(array($this, '_handleWarnedMethod'), E_USER_WARNING);
$toBeTestedObject = new ToBeTested();
$this->assertFalse($toBeTestedObject->simpleMethod(0));
restore_error_handler();
}
private function _handleWarnedMethod($errno, $errstr)
{
$this->assertEquals(E_USER_WARNING, $errno);
$this->assertEquals('Param is 0!', $errstr);
}
}
As always, error suppression isn't the best idea :)
The answer is that in PHPUnit 3.4.15 there is PHPUnit_Util_ErrorHandler class with handleError method which is executed when any error occurs. For error like E_USER_* this method always throws PHPUnit_Framework_Error, so execution of the rest of code is stopped.
The only way to prevent this is to disable user errors reporting, I think. It could be done like this:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
$toBeTestedObject = new ToBeTested();
// disable user errors reporting
$oldReportingLevel = error_reporting();
error_reporting($oldReportingLevel ^ (E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE));
// check the condition
$this->assertFalse($toBeTestedObject->simpleMethod(0));
// recover old error reporting level
error_reporting($oldReportingLevel);
}
}
Nearly 9 years later, and this question still comes up regularly.
You can use Netsilik/BaseTestCase (MIT License) to get extended functionality to assert errors/warnings are triggered as expected:
composer require netsilik/base-test-case
Testing for a E_WARNING:
<?php
namespace Tests;
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
/**
* {#inheritDoc}
*/
public function __construct($name = null, array $data = [], $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->_convertNoticesToExceptions = false;
$this->_convertWarningsToExceptions = false;
$this->_convertErrorsToExceptions = true;
}
public function test_whenWarningTriggered_weCanTestForIt()
{
$foo = new Foo();
$foo->bar();
self::assertErrorTriggered(E_WARNING, 'The warning string');
}
}

Categories