Try catch in a JUnit test

JavaUnit TestingJunitTry Catch

Java Problem Overview


I'm writing unit tests for an application that already exists for a long time. Some of the methods I need to test are build like this:

public void someMethod() throws Exception { 
   //do something 
}

If I want to test these methods I have to write something like this in my unit test:

@Test
public void someTest() {
   try {
      someMethod();
   }
   catch (Exception e) {
      e.printStackTrace();
   }
}

Is it a good practice to do this? Or is there an other way to test these methods?

I did some research on the internet and I found a few solutions with the @Rule annotation and @Test(expected=Exception.class), but that's not working (Eclipse keeps showing the someMethod() line in the test as wrong). I don't know if these are good solutions, because I'm pretty new to the whole unit testing story.

If someone who knows a lot about this could help me out, I would be really thankful.

Java Solutions


Solution 1 - Java

Since Exception is a checked exception, you either:

  • Have to catch the exception in a try...catch statement, or
  • Declare the exception to be thrown in the method itself.

What you have up there works fine, but my personal preference is to declare the exception to be thrown. This way, if an exception I'm not expecting is thrown during the run of the test, the test will fail.

@Test
public void someTest() throws Exception {
    // dodgy code here
}

If we need to see if a specific exception is thrown, then you have the option of using @Rule or adding the value to the @Test annotation directly.

@Test(expected = FileNotFoundException.class)
public void someTest() throws Exception {
    // dodgy code here
}

In JUnit 5, you can leverage Assertions.assertThrows to accomplish the same thing. I'm less familiar with this overall since it's not yet GA at the time of editing, but it appears to accept an Executable coming from JUnit 5.

@Test
public void someTest() {
    assertThrows(FileNotFoundException.class, () ->
         { dodgyService.breakableMethod() };
}

Solution 2 - Java

@Test
public void someTest() {
   try {
     someMethod();
   }
   catch (Exception e) {
     Assert.fail("Exception " + e);
   }
}

Is what you can do, if the exception should not occur. An alternative would be to throw the exception in the signature like this:

@Test
public void someTest() throws Exception {
     someMethod();
}

The difference is, that in one case the test will fail with an assertion exception and in the other case it will fail because the test crashed. (like somewhere in your code you get a NPE and the test will because of that)

The reason you have to do this, is because Exception is a checked exception. See Checked versus unchecked exception

The @Test(expected=Exception.class) is for tests, that want to test that the exception will be thrown.

@Test(expected=ArrayIndexOutOfBounds.class)
public void testIndex() {
   int[] array = new int[0];
   int var = array[0]; //exception will be thrown here, but test will be green, because we expect this exception

}

Solution 3 - Java

Do not catch your application's exception in your test code. Instead, declare it to be thrown upwards.

Because, when JUnit's TestRunner finds an exception thrown, it will automatically log it as an error for the testcase.

Only if you testcase expects that the method should thrown an Exception you should use @Test(expected=Exception.class) or catch the exception.

In other cases, just throw it upwards with,

public void someTest() throws Exception {

Solution 4 - Java

You can add exception in test method signature. Then, if you are testing whether exception is thrown, you have to use @Test(expected=Exception.class). In the test cases where exception has not to be thrown, test will pass successfully.

@Test
public void testCaseWhereExceptionWontBeThrown() throws Exception {
    someMethod(); //Test pass
}

@Test(expected = Exception.class)
public void testCaseWhereExceptionWillBeThrown() throws Exception {
    someMethod(); //Test pass
}

Solution 5 - Java

There are two main rules on how to process exceptions at Junit testers:

  1. If the exception was originated into the tested code:
  • If it was expected, declare it in the expected attribute of the Test annotation. Or, if further checks should be done on the exception object itself, catch it and ignore it. (In this case, there must be also a call to Assert.fail at the end of the try block, to indicate that the expected exception was not produced).
  • If it was not expected, catch it and execute Assert.fail. (A previous call to Exception.printStackTrace is also useful).
  1. If the exception was not originated into the tested code or it is not interesting to the test (for example, most of the IOExceptions are produced at network level, before the test could even be completed), rethrow it at the throws clause.

Why you should expect an exception in the tester? Remind: You should code one test method for every possible result on the tested code (in order to achieve a high code coverage): In your case, one method that must return successfully, and at least another one that must produce an Exception.

Solution 6 - Java

Three points about JUnit:

  • Tests should be precise, they should pass or fail unambiguously based solely on how the test inputs are set up.

  • Tests should have failures reported back into the framework.

  • Tests should not rely on having their output read.

Your example fails on all three counts. If an exception gets thrown or not, the test still passes. If an exception is thrown JUnit never finds out about it and can't include it in the test results. The only way to know something went wrong is to read what the test writes to stdout, which makes errors too easy to ignore. This is not a useful way to write tests.

JUnit was designed to make doing the right thing easy and to give developers useful feedback. If an exception gets thrown from a test method, it gets caught by the framework. If the test was annotated with an exception indicating that exception is expected, then the framework marks the test as passing. Otherwise the framework fails the test and records the stacktrace for reporting. The framework reports what assertions fail and what unexpected exceptions occurred so that everybody knows if the tests worked or not.

If you expect a test to succeed without throwing an exception, then if anything in the test can throw a checked exception, add throws Exception to the test method signature. Adding the throws to the signature doesn't say the method has to throw anything, it just lets any exceptions that happen to occur get thrown so that the test framework can catch them.

The only instance where you would actually catch the exception in the test is where you want to test assertions about the exception; for instance, you could test that the message on the exception is what you expect, or if the exception has a cause set on it. In that case you would add Assert.fail() at the end of the try-block so that not having an exception thrown will cause the test to fail.

It isn’t having a try-catch block that is so bad, it’s the absence of anything that will cause the test to fail that is bad.

When you write a test at first, make it fail. That way you prove to yourself that you know what the test is doing, and you confirm that, when there is a failure, you will be made aware of it.

Solution 7 - Java

What kind of exception is it? Is it

  1. an exception from doing something like using streams that won't happen in your unit test or
  2. an exception that can happen because of some kind of bad input?

If it's 1. I would just put it at the method signature level because a try-catch is serving no real purpose other than ceremony.

@Test
public void testFoo() throws Exception {
    // ...
}

If it's 2. it becomes a little more complicated. You need to ask yourself what should be happening if the Exception is thrown. Should the test fail? Is it expected? Is it irrelevant? Examples below of how to handle all of these. BEWARE: I only used Exception because you did. I hope it really isn't though because if it's possible for some other exception to be thrown other than the expected then these will be very wonky. If possible don't use Exception, use something more specific (in the junit and code).

// The below code assumes you've imported the org.junit.Assert class.

@Test
public void thisShouldFailIfExceptionCaught() {
    //Given...
    try {
        // When...
    } catch (Exception e) {
        Assert.fail();
    }
    // Then...
}

@Test
public void thisShouldPassOnlyIfTheExceptionIsCaught() {
    //Given...
    try {
        // When...
        Assert.fail();
    } catch (Exception expected) {}
    // No "then" needed, the fact that it didn't fail is enough.
}

@Test
public void irrelevantExceptionThatCouldBeThrown() {
    //Given...
    try {
        // When...
    } catch (Exception e) {}
    // Then...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNelschView Question on Stackoverflow
Solution 1 - JavaMakotoView Answer on Stackoverflow
Solution 2 - Javamorpheus05View Answer on Stackoverflow
Solution 3 - JavaCodebenderView Answer on Stackoverflow
Solution 4 - JavaHéctorView Answer on Stackoverflow
Solution 5 - JavaLittle SantiView Answer on Stackoverflow
Solution 6 - Javaпутин некультурная свиньяView Answer on Stackoverflow
Solution 7 - JavaCaptain ManView Answer on Stackoverflow