views:

155

answers:

4

We use JUnit 3 at work and there is no ExpectedException annotation. I wanted to add a utility to our code to wrap this:

 try {
     someCode();
     fail("some error message");
 } catch (SomeSpecificExceptionType ex) {
 }

So I tried this:

public static class ExpectedExceptionUtility {
  public static <T extends Exception> void checkForExpectedException(String message, ExpectedExceptionBlock<T> block) {
     try {
        block.exceptionThrowingCode();
        fail(message);
    } catch (T ex) {
    }
  }
}

However, Java cannot use generic exception types in a catch block, I think.

How can I do something like this, working around the Java limitation?

Is there a way to check that the ex variable is of type T?

+4  A: 

You could pass the Class object in and check that programatically.

public static <T extends Exception> void checkForException(String message, 
        Class<T> exceptionType, ExpectedExceptionBlock<T> block) {
    try {
       block.exceptionThrowingCode();
   } catch (Exception ex) {
       if ( exceptionType.isInstance(ex) ) {
           return;
       } else {
          throw ex;  //optional?
       }
   }
   fail(message);
}

//...
checkForException("Expected an NPE", NullPointerException.class, //...

I'm not sure if you'd want the rethrow or not; rethrowing would equally fail/error the test but semantically I wouldn't, since it basically means "we didn't get the exception we expected" and so that represents a programming error, instead of a test environment error.

Mark Peters
You should probably include a full usage example so people can see how it's no simpler or more readable than the standard idiom!
Kevin Bourrillion
@Pete Kirkham: `isAssignableFrom` takes a Class as a parameter, and essentially does the same thing as `isInstance` does on an object. `isInstance` returns true if the argument is assignment-compatible to the class, so it works on subclasses equally well.
Mark Peters
@Kevin Bourillion: I'm more interested in just the technical possibilities than advocating one way or another. I think your post correctly addresses the hazards of such an approach, and already upvoted you accordingly.
Mark Peters
A: 

Well, you could just catch Exception and rethrow if it's not an expected Exception. Though good coding practice usually dictates that the success path of code should not be defined by an Exception, so you might want to rethink your design.

ShadowRanger
I think for testing purposes it could have value.
Mark Peters
MarkPeters' answer is roughly what I was going for. Though it still has the whole "expected successful execution path requires exceptions, while error case doesn't." It also changes the execution slightly, so unexpected exceptions aren't rethrown, but instead trigger a call to fail. In your original code, you had three paths, expected exception, unexpected exception which goes up the stack, and no exception at all, calling fail. The Mark's code simplifies to expected exception and calling fail for unexpected exceptions and no exception cases.
ShadowRanger
Sorry, started my comment before you added yours. There is a potential use in testing, but I wanted to caution just in case this was for production code. Modern exception handling optimizes for the no exception case, so under success circumstances you'll be incurring overhead that your failure case avoids. Perverse, to say the least.
ShadowRanger
+3  A: 

I understand the impulse to try to simplify your exception-test idiom, but seriously: don't. Every possible choice you'll come up with is a cure that's worse than the disease. Especially JUnit 4's @ExpectedException nonsense! It is a too-clever frameworky solution, requiring everyone to learn how it works, as opposed to a plain self-evident bit of regular Java code. Worse, it gives you no way to wrap only the part of your test that you expect to throw the exception, so if an earlier setup step throws that same exception, your test will pass even though your code is broken.

I could write a long diatribe about this here (I'm sorry for not having enough time to), as we've had a lengthy discussion of this issue among Java engineers here at Google, and the consensus was that none of these crazy solutions are worthwhile. Get used to try/catch, it's really not that bad.

Kevin Bourrillion
Maybe I'm wrong but I think JUnit is pretty specific in its design and that within it, @ExpectedException isn't a too-clever solution. If your tests are so complicated that you don't know where your exceptions could be coming from then you aren't using JUnit right. They would say you might want to look at refactoring your code to better define what each of your classes/methods are doing. Given a good modular design I don't see why this should be an issue.Are there issues to this I am missing or understating?
Andrew Hubbs
A: 

You can also use an IDE that supports live template ( like IntellJ IDEA for instance ) and assign a shortcut like ee -> [tab] that inserts the try/catch/ignore for your and let you type the correct one.

like this

like this

OscarRyz
I do use IDEA but want to extract the common code into something that expresses the intent nicely
Alex Baranosky