3

I was recently asked in an interview on how can i write a junit unit test case to test for "OutOfMemoryError" and i have written the below code:-

public class OutOfMemoryError {
    public static void main(String[] args) {
        new OutOfMemoryError().throwError();
    }

    public void throwError() {
        List<String> list = new ArrayList<>();
        for (int i = 0;; i++)
            list.add("1");
    }
}

Junit Test Case:-

public class ExceptionTest {
    private OutOfMemoryError outOfMemoryError;

    @Before
    public void init() {
        outOfMemoryError = new OutOfMemoryError();
    }

    @After
    public void tearDown() {
        outOfMemoryError = null;
    }


    @Test(expected = Error.class)
    public void testError() {
        outOfMemoryError.throwError();
    }
}

The Interviewer told me that the Junit test case is incorrect. Can anyone please tell me the right way of writing it?

Hello Singh
  • 183
  • 1
  • 1
  • 7
  • You should ask (or should have asked) why the interviewer thought that the test was wrong. Given that 1) this is a totally artificial problem, 2) we don't actually know what the interviewer **actually** asked you, 3) we don't know what (if anything) the interviewer told you, and 4) we don't know your real motivation in asking this question, I don't think this is answerable. – Stephen C Mar 04 '18 at 06:50

1 Answers1

0

JUnit might not be able to correctly handle a test failure caused by an OutOfMemoryErrror because JUnit itself requires (very little but not nothing) heap memory to handle the failure.

Unfortunately, JUnit does not release the reference to the object under test (still referenced in a Statement object) before the Throwable handler kicks in, so even garbage collection cannot help here. See source of ParentRunner.runLeaf where this happens:

    } catch (Throwable e) {
        eachNotifier.addFailure(e);

EachTestNotifier.addFailure will then

    notifier.fireTestFailure(new Failure(description, targetException));

If there is no heap memory left, the Failure::new will throw another Error, preventing proper test failure handling.

Back to the interview: The interviewer may have wanted to hear that it is not (reliably) possible to test OutOfMemoryErrors with JUnit using a straight-forward approach due to the JUnit framework not being designed to do that as explained above. The interview might also have wanted to see a work-around like

@Test(expected = Error.class)
public void testError() {
    byte[] memoryRequiredByJUnitHandler = new byte[100_000];
    try {
        outOfMemoryError.throwError();
    } catch (Throwable t) {
        // free enough heap memory so JUnit can handle exception
        memoryRequiredByJUnitHandler = null;
        throw t;
    }
}

For more background information, you might also want to have a look at this answer to "When to catch java.lang.Error?".

halfbit
  • 3,414
  • 1
  • 20
  • 26
  • Yes. It is theoretically possible that this will make a difference. However, given the way that `ArrayList` behaves, it is unlikely. `ArrayList` grows the backing array exponentially, so by the time the OOME occurs, it will be trying to allocate a huge (contiguous) array. It is highly likely that the tiny allocation that the JUnit framework needs to make won't trigger a repeat of the OOME. – Stephen C Mar 04 '18 at 15:26
  • FWIW - this kind of interview question is pretty ridiculous. This kind of esoteric knowledge is not necessary for someone to be an effective Java programmer. – Stephen C Mar 04 '18 at 15:30