2

I am attempting to create an abstract data type called bag, which essentially takes in integers with the method add(int x) and removes an arbitrary integer with the method remove().

Then, I tried to create a custom exception for the remove() method since there is a chance that removal is done when there are already no items left in the bag. Thus, I created an exception class as such:

public class EmptyBagException extends Exception {
    public EmptyBagException(String message) {
        super(message);
    }
}

and proceeded to utilise this custom exception like so:

public int remove() {

    try {
        this.realRemoval();
    } catch (EmptyBagException e){
        System.out.println(e.getMessage());
    }

    return -1;
}

public int realRemoval() throws EmptyBagException {

    if (counter == 0) {
        throw new EmptyBagException("There are no items in the bag!");
    } else {
        ...
    }
}

Then, I tried to test the exception by doing this:

@Rule
public ExpectedException thrown = ExpectedException.none(); 

@Test
public void testThree() {

    IBag myBag = new BasicBag();
    myBag.remove();
    thrown.expect(EmptyBagException.class);
    thrown.expectMessage("There are no items in the bag!");

}

Unfortunately, this test failed and I got the message:

java.lang.AssertionError: Expected test to throw (an instance of sg.com.practice.adt.EmptyBagException and exception with message a string containing "There are no items in the bag!")

I am not sure why this is so...especially since my intended error message was indeed correctly printed to the console. Thanks in advance for the help!

umop apisdn
  • 653
  • 9
  • 20

1 Answers1

3

It's because you don't actually throw the exception out of remove():

public int remove() {

    try {
        this.realRemoval();
    } catch (EmptyBagException e){
        System.out.println(e.getMessage());
    }

    return -1;
}

In this case, the exception from realRemoval() is caught and handled by your try...catch block. The exception is thrown by realRemoval() then caught by your handler, the message is printed, and that's it: The exception isn't rethrown, and -1 is returned instead.

If you want it to rethrow the exception you'd have to do this instead:

public int remove() throws EmptyBagException { // <-- declare throws

    try {
        this.realRemoval();
    } catch (EmptyBagException e){
        System.out.println(e.getMessage());
        throw e; // <-- rethrow
    }

    return -1;

}

Or just get rid of your output message and let it happen naturally:

public int remove() throws EmptyBagException { // <-- declare throws

    this.realRemoval(); // <-- may throw

    return -1;

}

Also note that you'll want to set up thrown before calling remove() in your test function, as if remove() throws, then the test function will throw and won't actually get past that point to set up thrown.

By the way, don't you mean to return the value of realRemoval() rather than -1?

Jason C
  • 38,729
  • 14
  • 126
  • 182
  • Thank you very much for your prompt reply! Unfortunately I still have two questions for you: 1) What do you mean by "swallowed by your exception handler"? Isn't the point of the catch block to intercept the exception thrown? Why do I have to rethrow the exception? 2) Is there a way whereby I can avoid throwing in remove? This is because I actually use an interface as well (IBag), and by throwing in remove I think I have to wrap all my remove tests in try-catch blocks...right? Thanks again for your help! – umop apisdn Feb 22 '17 at 03:55
  • @umopapisdn Of course you have to rethrow the exception. Your test condition is specified to verify that `remove()` throws an exception. So an exception must ultimately be thrown out of `remove` to satisfy the test, assuming you want your test to pass. If that isn't the intended behavior of `remove` then your test conditions are incorrect. You might want to have a read of https://docs.oracle.com/javase/tutorial/essential/exceptions/ by the way to understand what `catch` does. – Jason C Feb 22 '17 at 04:01
  • @umopapisdn As for #2... what leads you to believe you'd have to wrap all your remove tests in try-catch blocks? – Jason C Feb 22 '17 at 04:03
  • For #1, I was just wondering about the need to re-throw the exception since I already threw it from within the `realRemoval()` method...I've been reading Oracle's Java Tutorials and other overflow posts but I've not seen that appearing before so I am pretty puzzled by it... – umop apisdn Feb 22 '17 at 04:15
  • As for #2, do you mean I can simply state `int remove() throws EmptyBagException` in the interface, and then in my tests, do something like `public void testOne throws ExmptyBagException` if testOne contains the `remove()` method? Also, I can actually shift everything in `realRemoval` to `remove()` under the try block, and return from `remove()` directly, right? Thanks to your suggestions, I have already managed to write and pass the test, but I just hope to better understand exceptions in Java. Thanks a lot :) – umop apisdn Feb 22 '17 at 04:15
  • @umopapisdn If you catch the exception inside `remove`, then ... you've caught the exception. So since you caught it, if you don't throw it again, that's the end of that, and your test fails. It's like if somebody throws you a ball and you catch it and then just stand there. You don't get to be surprised that somebody else doesn't end up with the ball. You'd have to actually throw it to somebody else instead of just standing around holding a ball. The Oracle tutorial has everything you need, you may want to read it again then try some experiments. – Jason C Feb 22 '17 at 04:25
  • And yes you can of course put `throws` declarations on interface methods. What a method throws is as much a part of the interface as anything else. If you have any other issues please ask a new question here. – Jason C Feb 22 '17 at 04:27
  • I see, I see, that's a cool analogy. Thanks a lot, I will continue to work on my exceptions! – umop apisdn Feb 22 '17 at 04:29
  • @umopapisdn There is a reason they use the words "throw" and "catch". :) – Jason C Feb 22 '17 at 04:31