views:

386

answers:

2

When testing for exceptions with PHPUnit, what is the best way to require that every statement or assertion must throw an exception in order for the test to pass?

I basically want to do something like this:

public function testExceptions()
{

    $this->setExpectedException('Exception');

    foo(-1); //throws exception
    foo(1); //does not throw exception

}

//Test will fail because foo(1) did not throw an exception

I've come up with the following, which does the job, but is quite ugly IMO.

public function testExceptions()
{

    try {
     foo(-1);
    } catch (Exception $e) {
     $hit = true;
    }

    if (!isset($hit))
     $this->fail('No exception thrown');

    unset($hit);

    try {
     foo(1);
    } catch (Exception $e) {
     $hit = true;
    }

    if (!isset($hit))
     $this->fail('No exception thrown');

    unset($hit);

}
+1  A: 

This doesn't make sense to me.

I guess you are trying to test multiple separate things with one test case which is bad practice.

When foo() throws the expected exception the test case is successful and bar() won't run.

Just create two separate test cases which is a lot less code then what you produced in the second listing.

Or explain why it would make sense to run bar(), after foo() failed with an exception, when it will throw an exception too.

jitter
I was trying to test calling the same function with different arguments (and I edited my example to better reflect this).An example in the PHPUnit manual shows multiple related assertions being made in a single test, so I was trying to replicate this but with exceptions.
etheros
+1  A: 

As exceptions are such big events in the program flow, testing multiple ones in a single test is problematic.

The easiest thing is is to simply split it into two tests - the first requires an exception to be able to pass, the second simply runs, and would fail it it did throw one. You could add some other tests in the second if you wanted (confirming a return value maybe), but I'd be inclined to make sure it still did just the one essential thing, according to the naming of it.

/**
 * @expectedException Exception
 */
public function testBadFooThrowsException()
{
    // optional, can also do it from the '@expectedException x'
    //$this->setExpectedException('Exception');
    foo(-1); //throws exception -- good.
}

public function testFooDoesNotThrowException()
{
    foo(1); //does not throw exception
}
Alister Bulman
I definitely see your point, although it still feels a bit strange to have multiple tests when the point of each one is to ensure that an exception is thrown.
etheros