3

I am trying to follow this tutorial JUnit 5: How to assert an exception is thrown?

I use Java 10, IntelliJ 2018 and Junit 5.

I make a calculator app that adds 2 fractions. It checks whether the input has 0 in the denominator.

When I run the test The exception Message get printed out "Undefined Math Expression" but my IDE says "Expected java.lang.Throwable to be thrown, but nothing was thrown." I think there is some problem with the scope of my code? I'm a newbie, please be kind. I provided the code and the test below:

public class Calculator {
    public static int[] calculate (int firstNumerator, int firstDenominator, int secondNumerator, int secondDenominator) {

        String exceptionMessage = "Undefined Math Expression";
        int resultNumerator;
        int resultDenominator;
        int[] result = new int[2];

        resultNumerator =  (firstNumerator * secondDenominator) +
                (secondNumerator * firstDenominator);
        resultDenominator = firstDenominator * secondDenominator;

        try {
            if (resultDenominator == 0) {
                  throw (new Throwable(exceptionMessage));
            } else {
                result[0] = resultNumerator;
                result[1] = resultDenominator;
            }
        } catch (Throwable e) {
           System.out.println(e.getMessage());
        }

        return result;
    }
}

The test:

class CalculatorTest {
    @Test
    void denominatorContainsZero() {
        assertThrows(Throwable.class, () -> {
            Calculator.calculate(0,0,0,0);
        });
    }
}
khelwood
  • 55,782
  • 14
  • 81
  • 108
Team
  • 587
  • 2
  • 9
  • 21
  • You should catch `Throwable` rarely; you should (effectively) *never* throw `Throwable`. In this case, exceptions are totally unnecessary: just print the message. – Andy Turner Oct 02 '18 at 07:48

3 Answers3

9

The misunderstanding here appears to be in what JUnit can actually see.

JUnit isn't magical: it's just plain old Java. It can't see inside your methods to see what they are doing. All it can see is what any other code can see when it executes a method: the return value and uncaught exceptions (as well as any side effects of the method, if they are visible to the calling code).

Your method here doesn't throw an exception from the perspective of a caller: internally, it throws the exception, but it catches and handles it.

If you want JUnit to test that an exception is thrown, you need to not catch that exception.

It is never (*) the right thing to do to throw an exception and then catch and handle it yourself. What's the point? You can simply do the thing you do to handle it, without throwing the exception. Exceptions are expensive to throw, because of the need to capture the entire stack trace.

Throwable is never (*) the right exception to throw. It's the exception "equivalent" of returning Object: it conveys no type information about the exception to the caller, who then either has to do a lot of work to try to handle it; or, more realistically, should just propagate it themselves. IllegalArgumentException is the right exception to throw here, if you actually needed to throw (and not catch) an exception.

Throwable is rarely the right thing to catch. Throwable is a supertype of both Exception and Error, so you might unintentionally catch an Error, like OutOfMemoryError, which shouldn't be caught because there is nothing reasonable to do except crash your program. Catch the most specific type you can; which also means that you should throw the most specific type you can (or, at least, a type appropriate to the abstraction).


(*) This is "never" as in "ok, there are a limited number of circumstances where it may be appropriate". But unless you understand what these are, don't.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Im trying to do it sir but it forces me to catch it even though I added "throws Throwable" to my method signature. It stills want me to catch. – Team Oct 02 '18 at 08:03
  • 1
    @user3270418 that's because you are throwing a checked exception. If you throw an unchecked exception, like `IllegalArgumentException`, you don't need to declare `throws`. – Andy Turner Oct 02 '18 at 08:06
  • @ Andy Turner Ok sir, you mean to remove both try and catch then change the Throwable into IllegalArgumentException. it throws exception now. but need to remove both try and catch. So in conclusion, If there is a try, there need to be a catch right? – Team Oct 02 '18 at 08:16
  • 1
    @user3270418 remove the try, remove the catch, and `throw new IllegalArgumentException(whateverMessage);`. – Andy Turner Oct 02 '18 at 08:25
6

The Throwable is catched by try catch block, so Junit can not access it. Try remove the try catch block.

xingbin
  • 27,410
  • 9
  • 53
  • 103
  • I tried to delete the: catch (Throwable e) { System.out.println(e.getMessage()); } but the system says Unhandled exception. The system forces me to handle it. How can i get my function to throw exception without handle it? – Team Oct 02 '18 at 07:49
  • The workaroud might be change method signature to `public static int[] calculate (int firstNumerator, int firstDenominator, int secondNumerator, int secondDenominator) throws Throwable` – xingbin Oct 02 '18 at 07:51
  • @user3270418 But, I do not suggest to use `Throwable`. You might want to use `IllegalArgumentException`. – xingbin Oct 02 '18 at 07:51
  • i tried to add "throws Throwable" to my method signature but it stills want me to catch it o_O. it says "Error:(15, 63) java: unreported exception java.lang.Throwable; must be caught or declared to be thrown" – Team Oct 02 '18 at 07:59
2

You are not actually throwing exception, you are catching it. For this to work, you should remove try catch block.