2

In below code a checked exception is being thrown by ClassToTest.throwsException(). My preference is to add throws exception to the JUnit method signature instead of wrapping the code that throws an exception in a try catch. This preference is just based on the fact that I think its neater. Is there a coding convention I'm breaking here? Is there a difference in functionality from a testing point of view in throwing the exception versus try/catch in the testing method?

import org.junit.Test;

public class TestingClass {


    @Test
    public void getDataTest() throws Exception {

        new ClassToTest().throwsException();

    }

}

versus :

import org.junit.Test;

public class TestingClass {


    @Test
    public void getDataTest() {

        try {
            new ClassToTest().throwsException();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

ClassToTest.java :

public class ClassToTest {

    public void throwsException() throws Exception {

    }
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
blue-sky
  • 51,962
  • 152
  • 427
  • 752
  • 2
    If you catch it, the test case won't fail when it's thrown. Only catch it if you intend to test that the exception was thrown (but make sure the test would fail if it isn't thrown, e.g. with `fail()` after the statement that should throw). – Andy Turner Jan 11 '20 at 21:05
  • What is the expected behaviour? Do you expect an `Exception` to be thrown or not? – Turing85 Jan 11 '20 at 21:09
  • @Turing85 no, i don't expect an exception to be thrown. – blue-sky Jan 11 '20 at 21:15
  • Then I do not see any benefit in adding the `try`-`catch`-block. It only increases the complexity of the code. If an exception is thrown, the test will fail. Even worse one would have to add a `fail()` to the `catch`-block to explicitly make the test fail (additional source of error if one forgets to add the `fail()`). – Turing85 Jan 11 '20 at 21:17
  • Does this answer your question? [How do you assert that a certain exception is thrown in JUnit 4 tests?](https://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests) – Progman Jan 11 '20 at 21:20
  • @Progman no, not a duplicate. OP wants to let the test fail if any `Exception` is thrown. – Turing85 Jan 11 '20 at 21:24

2 Answers2

3

Simply add the throws clause to the method declaration and let the exception bubble up, as written in the JUnit4 FAQ - How do I write a test that fails when an unexpected exception is thrown?:

Declare the exception in the throws clause of the test method and don't catch the exception within the test method. Uncaught exceptions will cause the test to fail with an error.

The following is an example test that fails when the IndexOutOfBoundsException is raised:

@Test
public void testIndexOutOfBoundsExceptionNotRaised()
    throws IndexOutOfBoundsException {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

IndexOutOfBoundsException might not be a good example here, since it is a RuntimeException, but the approach is the same for other exceptions.

Progman
  • 16,827
  • 6
  • 33
  • 48
3

If one does not expect an Exception to be thrown, there is no added benefit in adding a try-catch block to the code. Even worse, only adding a try-catch block will lead to the test passing. To make the test fail, one would have to add a call to fail() to make the test actually fail. This is a possible source for error (if one forgets to call fail()).


For completeness sake, I will talk shortly about how to verify that a certain Exception has been thrown. There are three approaches that come to my mind.

In the first attempt, one could use try-catch, but with an added fail() in the try-block, just after the calls that should throw the expected Exception. In the catch-block, one would then catch the expected Exception. All other Exceptions would be re-thrown and thus the test would fail. This has the same downsides as its sibling mentioned above.

Second, there is the JUnit4 way by annotating the test itself with @Test(expected = ExpectedException.class). This seems neat at first, but breaks the Given-When-Then structure of tests, often leading to tests looking like this:

@Test(expected = ArrayIndexOutOfBoundsException.class)
public void test() {
    // GIVEN
    final int[] array = new int[10];

    // WHEN
    final int value = array[10];

   // THEN: an ArrayIndexOutOfBoundsException should be thrown
}

which is okay-ish.

Lastly, there is the JUnit5 way by wrapping the actual call into a call of assertThrows(...):

@Test(expected = ArrayIndexOutOfBoundsException.class)
void test() {
    // GIVEN
    final int[] array = new int[10];

    // WHEN
    final Exception e = assertThrows(ArrayIndexOutOfBoundsException.class,
            () -> {
                int value = array[10];
            }
    );

   // THEN
   assertTrue(e.getMessage().contains("10"));
}

While this still does not properly separates the WHEN from the THEN (I think this is not possible in Java), it gives the added benefit to allow checking specific parts of the Exception, e.g. the message1.

I would suggest this article over at Baelung as a further read.


1This is also possible in JUnit4, but is either done through an explicit try-catch block or through a very cumbersome mechanism that definitively breaks the Given-When-Then structure. For more information, please check the article over at Baelung mentioned above.

Turing85
  • 18,217
  • 7
  • 33
  • 58
  • JUnit 4 also provides a `org.junit.rules.ExpectedException` test-rule where you can declaratively specify which exception (and message or other attributes) you expect, and [since JUnit 4.13](https://github.com/junit-team/junit4/blob/master/doc/ReleaseNotes4.13.md), it also has `assertThrows`. – Mark Rotteveel Jan 12 '20 at 09:40