14

I'm not getting how to unit test Exceptions with PHPUnit.

Please see my method with the Exception:

    public function getPhone($html, $tag = 'OFF', $indicative, $number_lenght) {

        // .. code

        if ($tag <> 'OFF') {

            $html = $doc[$tag]->text(); // Apanho apenas o texto dentro da TAG
                if (empty($html)) {
                    throw new Exception("Nao foi possivel apanhar qualquer texto dentro da TAG, Metodo em causa: getPhone()");
                }               
        }

        // .. code
    }

And now my PHPUnit Test:

<?php

require_once '../Scrap.php';

class ScrapTest extends PHPUnit_Framework_TestCase
{

    protected $scrap;

    // Setup function to instantiate de object to $this->scrap
    protected function setUp()
    {
        $this->scrap = new Scrap;
    }

    /**
    * @covers Scrap::getPhone
    * @expectedException Exception
    *
    */
    public function testGetPhone() {

        // Variables1
        $array_static1 = Array(0 => 218559372, 1 => 927555929, 2 => 213456789, 3 => 912345678);
        $phone_list1   = '</div>A Front para<br /><br /><br /><br /><br /><br />-Apoio;<br />-Criação;<br />-Campanhas;<br />-Promoções<br /><br /><br />CONDIÇÕES:<br /><br />Local de Trabalho: Es<br />Folgas: Mistas<br /><br /><br /><br />ordem 500€<br /><br /><br /><br />Mínimos:<br /><br />- Conhecimentos;<br />- Ensino ;<br />-INGLÊS.<br /><br /><br /><br />Candidaturas: <br />email@ffff.es<br />218559372 | 927 555 929 | <br />RH<br />Rua C. Sal. 40<br />1000-000 Lisboa<br /><br /><br />+351 21 3456789 | (351) 912345678';

        // Variables2
        $array_static2 = Array(0 => 'NA');
        $phone_list2   = "";

        // .. more tests

        // Test Exception, Tag not found
        if (TRUE) {

            // Bloco try/catch para confirmar que aqui lança excepção
            try {            
                    $this->scrap->getPhone($phone_list1, 'hr', '351', '9');        
                }         
            catch (Exception $expected) {
                    return;        
                }         

            $this->fail('An expected exception has not been raised.');  
        }



    }
}
?>

If I run the test I got "Failure":

1) ScrapTest::testGetPhone
Expected exception Exception

FAILURES!
Tests: 1, Assertions: 5, Failures: 1.

The exception raises but I don't want to get failure in the PHPUnit, If the Exception raise, I want to get the test OK.

Can you give me some clues?

Best Regards,

Kamafeather
  • 8,663
  • 14
  • 69
  • 99
André
  • 24,706
  • 43
  • 121
  • 178
  • 1
    possible duplicate of [How to use PHPUnit's setExpectedException()?](http://stackoverflow.com/questions/4646298/how-to-use-phpunits-setexpectedexception) – zerkms Jan 10 '11 at 12:33
  • was asked just a couple minutes ago – Gordon Jan 10 '11 at 12:55

2 Answers2

33

You are doing too much there.

Either use: @expectedException Exception

OR: try / catch / $this->fail

The way you are doing it right now says "catch that exception and THEN expect the code to throw another one!"

The first way is cleaner in my opinion because it's only 1 line against 5 (or even more) lines of code and it's less error prone.

/**
* @covers Scrap::getPhone
* @expectedException Exception
*
*/
public function testGetPhone() {

    // Variables1
    $array_static1 = Array(0 => 218559372, 1 => 927555929, 2 => 213456789, 3 => 912345678);
    $phone_list1   = '...';

    // Variables2
    $array_static2 = Array(0 => 'NA');
    $phone_list2   = "";

    // .. more tests

    // Bloco try/catch para confirmar que aqui lança excepção
    $this->scrap->getPhone($phone_list1, 'hr', '351', '9');        

That should do it.

danronmoon
  • 3,814
  • 5
  • 34
  • 56
edorian
  • 38,542
  • 15
  • 125
  • 143
  • 6
    You can also use `$this->setExpectedException('ExceptionTypeGoesHere')` at the top of your test method, as well as the two listed above. I think this is the cleanest approach. – TeaPow Mar 13 '13 at 15:36
  • Exactly. Take a look at [dotnetCarpenter's comment](https://stackoverflow.com/questions/4646822/how-to-unit-testing-exceptions-with-phpunit#comment74684159_31800400) for updated guidelines. – Kamafeather Nov 08 '18 at 13:20
14

There are two ways to test thrown exceptions but it depend on your needs. If you don't care about the content/properties of the exception (i.e. code, message, etc), then you can do:

$this->setExpectedException('MyApp\Exception');
$object->someFailingCodeWithException();

Else, if you need to use exception properties for assertion (i.e. code), then you can do the try-catch-fail:

try {
    $object->someFailingCodeWithException();
} catch (MyApp\Exception $e) {
    $this->assertEquals($e->getCode(), 100);
    return;
}

$this->fail();

Notice the return statement inside the catch block. The $this->fail(); statement will/must be only called once there is no exception raised. Thus, this test case fails because it should test the exception which is not thrown in the first place.

Aldee
  • 4,439
  • 10
  • 48
  • 71
  • 2
    At least from phpunit 6.1 this doesn't work anymore. _You should be as specific as possible when testing exceptions. Testing for classes that are too generic might lead to undesirable side-effects. Accordingly, testing for the Exception class with @expectedException or setExpectedException() is no longer permitted._ https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.exceptions – dotnetCarpenter May 06 '17 at 16:43
  • 1
    in PHPunit 6+ we need to use public function testGetPhone() { $this->expectException(\InvalidArgumentException::class); some code cause exception here } – Eugene Kaurov Sep 14 '17 at 13:18
  • Works like a charm – Invincible Jul 20 '18 at 15:41