Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing exceptions in PHPunit always fails

I have few custom exceptions in PHP:

class MainException extends Exception {};
class ExceptionOne extends MainException {};
class ExceptionTwo extends MainException {};

And I'm using them in my class in two simple methods:

public function firstFunction($param) {
    if ($some_condition) {
        // do whatever
    } else {
        throw new ExceptionOne();
    }
}

public function secondFunction($param) {
    if ($some_condition) {
        // do whatever
    } else {
        throw new ExceptionTwo();
    }
}

I also have a PHPUnit test for both of exceptions, similar to this:

public function testFirstException() {
    try {
        // anything
    } catch (Exception $e) {
        $this->assertType('ExceptionOne', $e);
        $this->assertType('MainException', $e);
    }
}
public function testSecondException() {
    try {
        // anything
    } catch (Exception $e) {
        $this->assertType('ExceptionTwo', $e);
        $this->assertType('MainException', $e);
    }
}

If I test my class in browser and intentionally make my functions fail (with same stuff as in PHPUnit test) I can see ExceptionOne and ExceptionTwo are raised whenever needed. However, when I test it with PHPUnit I always get a failure:

1) testSecondException(SomeTest)
Failed asserting that <PHPUnit_Framework_ExpectationFailedException> is an instance of class "ExceptionTwo".
C:\test.php:67

Line 67 is
$this->assertType('ExceptionTwo', $e);
and it fails here no matter what I try. I'm pretty sure that my condition in secondFunction works correct. The first test (testFirstException) works perfectly and I never get a failure like in the second one.
I'd like to add that I shouldn't change PHPUnit test here!

What am I doing wrong here??

like image 984
errata Avatar asked Nov 02 '12 15:11

errata


1 Answers

From your comments to cbuckley's excellent answer, is appears that the test was written like this:

public function testFirstException() {
    try {
        // some code that is supposed to throw ExceptionOne
        $this->assertTrue(false, "Test failed");
    } catch (Exception $e) {
        $this->assertType('ExceptionOne', $e);
        $this->assertType('MainException', $e);
    }
}

The assertTrue is used to make sure the test fails if the exception isn't thrown during the test. However, this doesn't work because the failed assertion throws a different exception type which causes the error message to be confusing.

Failed asserting that <PHPUnit_Framework_ExpectationFailedException> 
is an instance of class "ExceptionOne".

You can fix this by using either @expectedException or setExpectedException. Not only will the test pass when ExceptionOne is thrown, but it will fail when it isn't thrown or some other exception type is thrown.

/**
 * @expectedException ExceptionOne
 */
public function testFirstException() {
    // some code that is supposed to throw ExceptionOne
}

When no exception is thrown the error message is

Failed asserting that exception of type "ExceptionOne" is thrown.

and when different type of exception is thrown you'll see

Failed asserting that exception of type "Exception" matches
expected exception "RuntimeException".

This method of testing exceptions is

  • easier to write,
  • easier to read,
  • easier to debug when the test fails,
  • and correct.
like image 122
David Harkness Avatar answered Sep 22 '22 22:09

David Harkness