6

I have a set of tests, and I want to test that my classes throw exceptions at the right time. In the example, my class uses the __get() magic method, so I need to test that an exception is thrown when an invalid property is retrieved:

function testExceptionThrownWhenGettingInvalidProperty() {
  $obj = new MyClass();
  $this->setExpectedException("Property qwerty does not exist");
  $qwerty = $obj->qwerty;
}

The class throws an error as it should, but instead of just getting a pass, the exception isn't caught!

There was 1 error:

1) QueryTest::testExceptionThrownWhenGettingInvalidProperty
Exception: Property qwerty does not exist

I was using SimpleTest before, and $this->expectException(new Exception("Property qwerty does not exist")); worked just fine. I know there are other methods (@expectedException and try-catch), but this one should work and it looks a lot cleaner. Any ideas how I can make this work?

2 Answers 2

13

It's not looking for the text in the exception, it's looking for the name of the exception class... Docs

$this->setExpectedException('Exception');

It's quite handy when you're using SPL Exceptions, or custom exception classes...

6
  • Thanks! I completely overlooked that because I was used to the SimpleTest way of doing it. Do you know if there's an easy way to test for the text?
    – Nathan
    Commented Nov 11, 2010 at 20:45
  • @Nathan, @ircmaxell: See my answer, there is a better way of doing this, including testing for text.
    – ryeguy
    Commented Nov 11, 2010 at 21:09
  • @ircmaxell: I disagree with the message not mattering. You don't see any well designed languages or frameworks who create a new exception for every new error that pops up. That would just be too much work.
    – ryeguy
    Commented Nov 11, 2010 at 21:13
  • 1
    setExpectedException actually takes 3 parameters and last 2 are optional - if you want to test for for exception type AND message contents, then $this->setExpectedException('Exception', 'Property qwerty does not exist'); If you have and want to test for exception code as well, then that would be the third parameter. Commented Nov 11, 2010 at 21:14
  • Oh, I understand that @ryeguy, but you also don't catch (FooException $e) { switch ($e->getMessage()) { case 'blah1': ... The exception should define what went wrong (InvalidArugmentException as an example), and the code should figure out how to handle it based on the call. The messages are there for debugging... Not error capturing in the code (then the message would become a magic string)...
    – ircmaxell
    Commented Nov 11, 2010 at 21:16
13

Adding to ircmaxell's answer, there is actually a simpler way of doing this:

/**
 * @expectedException MyExceptionClass
 * @expectedExceptionMessage Array too small
 */
public function testSomething()
{
}

The @expectedException the class name of the exception to expect, and @expectedExceptionMessage is a substring of the exception message to expect (that's right, you don't have to have the entire message).

If you prefer to not use docblock annotations, both of these are actually available as methods on the test case.

2
  • 4
    +1... I wasn't aware of the @expectedExceptionMessage docblock element... Learn something new every day...
    – ircmaxell
    Commented Nov 11, 2010 at 21:12
  • @ircmaxell: Yeah, I just discovered it a bit ago myself. There is no mention of it anywhere except for under the "Annotations" appendix. Even then, there is no docs on it, it just lists its name.
    – ryeguy
    Commented Nov 11, 2010 at 21:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.