← Back to libraryQuestion 278 of 468
🔬TestNG & JUnitIntermediate

Testing Expected Exceptions

📌 Definition:

Tests often verify that code THROWS the right exception. TestNG uses @Test(expectedExceptions = X.class); JUnit 5 uses assertThrows(X.class, () -> ...), which also lets you assert on the exception message.

📖 Detailed Explanation:

TestNG: @Test(expectedExceptions = IllegalArgumentException.class) passes if that exception is thrown, and expectedExceptionsMessageRegExp can check the message. JUnit 4 used @Test(expected=...); JUnit 5 prefers assertThrows(IllegalArgumentException.class, () -> service.call(bad)), which RETURNS the thrown exception so you can assert on its message/cause — more precise because it scopes exactly WHICH line should throw (annotation-level expected can't tell which statement threw). Testing negative paths (validation errors, auth failures) is essential; assertThrows is the modern, precise approach.

🔑 Key Points:
  • TestNG @Test(expectedExceptions=X.class) (+ message regex)
  • JUnit5 assertThrows(X.class, () -> ...) returns the exception
  • assertThrows scopes exactly which statement must throw
  • Assert on message/cause for precise negative tests
🌍 Real-World Example:

Verifying validation: assertThrows(ValidationException.class, () -> service.create(invalid)) confirms the exact call throws, then assertEquals('email required', ex.getMessage()) checks the message.

🎯 Scenario-Based Interview Question:

Why is JUnit 5's assertThrows often preferred over the annotation-level 'expected exception' approach?