QA Interview Hub · Knowledge base · Quizzes

Java basics

Exceptions

Checked and unchecked exceptions, try-with-resources, AssertionError

Everything that can be thrown extends Throwable. There are two branches:

  • Error — serious JVM failures (OutOfMemoryError) and AssertionError, which assertion libraries throw. They are normally not caught.
  • Exception — what can be handled. Checked ones (for example IOException, SQLException) the compiler forces you to handle or declare with throws. Unchecked ones are subclasses of RuntimeException (NullPointerException, IllegalArgumentException, IndexOutOfBoundsException); handling is optional.

Custom exceptions. In test code they are usually unchecked so as not to clutter signatures: class TestDataException extends RuntimeException. Always pass the cause so the stack trace is not lost:

try {
    return mapper.readValue(Files.readString(file), User.class);
} catch (IOException e) {
    throw new TestDataException("Failed to load test data: " + file, e);
}

try-with-resources automatically calls close() on objects that implement AutoCloseable (streams, database connections, clients), even if an exception occurred. Closing goes in reverse order of opening; an exception from close() does not overwrite the main one but is attached as suppressed.

What not to do in tests:

  • An empty catch or only printStackTrace(): the failure vanishes, the test is green and the scenario was not executed.
  • catch (Throwable) or catch (Exception) around assertions: AssertionError is an Error, so the first form swallows it (the test "passes") while the second does not catch it and the test correctly fails.
  • throws Exception on every method just to get rid of the compiler.

When to catch: when you can really react (a retry, an expected negative scenario, adding context). An expected failure is checked with assertThatThrownBy(...) (AssertJ) or assertThrows (JUnit), not with try/catch and fail().