JUnit Expected Exception Test: @Test(expected)
โก Smart Summary
JUnit exception testing confirms that a method throws the error it is supposed to throw, using the optional expected parameter of the @Test annotation in JUnit 4 and the assertThrows method in JUnit 5.
JUnit provides the facility to trace the exception and also to check whether the code is throwing the expected exception or not.
JUnit 4 provides an easy and readable way for exception testing. You can use:
- The optional parameter (expected) of the @Test annotation, and
- To trace the information, “fail()” can be used.
While testing an exception, you need to ensure that the exception class you are providing in that optional parameter of the @Test annotation is the same one the method actually throws. This is because you are expecting an exception from the method you are unit testing; otherwise our JUnit test would fail.
Example: @Test(expected = IllegalArgumentException.class)
By using the “expected” parameter, you can specify the exception name our test may throw. In the example above, you are using “IllegalArgumentException“, which will be thrown by the test if a developer uses an argument that is not permitted.
Example using @Test(expected)
Let us understand exception testing by creating a Java class with a method that throws an exception. You will handle it and test it in a test class. Consider JUnitMessage.java, which has a method performing a mathematical operation. The division on line 14 divides by zero, so the method always throws an “ArithmeticException”. See below:
package guru99.junit; public class JUnitMessage{ private String message; public JUnitMessage(String message) { this.message = message; } public void printMessage(){ System.out.println(message); int divide=1/0; } public String printHiMessage(){ message="Hi!" + message; System.out.println(message); return message; } }
Code Explanation:
- Code Line 7: Creating a parameterized constructor with field initialization.
- Code Line 11-14: Creating a method for the mathematical operation.
- Code Line 18: Creating another method to print a message.
- Code Line 20: Creating a new string to print a message.
- Code Line 22: Printing the new message created in line 20.
Let us create a test class for the above Java class to verify the exception.
See below the test class that unit tests the exception (ArithmeticException here) thrown from the above Java class:
AirthematicTest.java
The screenshot below shows the same test in the editor, where the file is saved as AirthematicTest1, with the expected parameter highlighted on line 13:
package guru99.junit; import static org.junit.Assert.assertEquals; import org.junit.Test; public class AirthematicTest { public String message = "Saurabh"; JUnitMessage junitMessage = new JUnitMessage(message); @Test(expected = ArithmeticException.class) public void testJUnitMessage(){ System.out.println("Junit Message is printing "); junitMessage.printMessage(); } @Test public void testJUnitHiMessage(){ message="Hi!" + message; System.out.println("Junit Message is printing "); assertEquals(message, junitMessage.printHiMessage()); } }
Code Explanation:
- Code Line 13: Using the @Test annotation to create our test. As you execute the method of the class above, it will invoke a mathematical operation. Here ArithmeticException is expected, so you are listing it out as a parameter in @Test.
- Code Line 17: Invoking printMessage() from JUnitMessage.java.
- Code Line 21-22: Creating another test method to check the Hi message, this time without an expected parameter.
The class holds two test methods, so a single run executes both of them: the one that expects ArithmeticException and the one that asserts on the returned string.
Note: this example travels under three names in the original material โ the listing calls the class AirthematicTest, the editor screenshot shows AirthematicTest1, and the result view reports JunitTestExample. The code is identical in each; only the file name differs.
Let us execute it and verify the result. The JUnit view below reports the run of JunitTestExample.java.
Output:
Here is the output, which shows a successful test with no failure trace as given below:
Both methods are green. The first passes because the ArithmeticException it declared did arrive, and the second passes because the returned string matched. Had the division never thrown, JUnit would have failed the first method with the message “Expected exception: java.lang.ArithmeticException”.
Three Ways to Test an Exception in JUnit 4
The expected parameter is the shortest of the three JUnit 4 idioms, but it is not always the right one. The table compares them on the two questions that decide the choice: can you assert on the message, and do you know which line threw?
| Approach | Asserts the message? | Pins the throwing line? | Best for |
| @Test(expected = X.class) | No | No โ any line in the method may throw | Short tests where only the exception type matters. |
| try / fail() / catch | Yes, inside the catch block | Yes โ only the guarded call is watched | Tests that must inspect the message or the cause. |
| @Rule ExpectedException | Yes, through expectMessage() | No | Legacy suites already built on rules. |
The fail() idiom the introduction mentions looks like this. If the call does not throw, fail() runs and the test reports the message you wrote:
@Test public void testDivideByZero() { try { junitMessage.printMessage(); fail("Expected an ArithmeticException"); } catch (ArithmeticException e) { assertEquals("/ by zero", e.getMessage()); } }
The ExpectedException rule sits between the two. It was deprecated in JUnit 4.13 in favour of the assertThrows method described next, so new JUnit 4 tests should not adopt it.
How to Test Exceptions in JUnit 5 with assertThrows()
JUnit 5 removes the expected parameter from @Test altogether. Jupiter supplies assertThrows(), which takes the exception class and a lambda holding the code under test. It returns the exception it caught, so the message, the cause and any custom field can be asserted afterwards.
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; public class AirthematicJupiterTest { JUnitMessage junitMessage = new JUnitMessage("Saurabh"); @Test public void testJUnitMessage() { ArithmeticException thrown = assertThrows( ArithmeticException.class, () -> junitMessage.printMessage()); assertEquals("/ by zero", thrown.getMessage()); } }
Three related assertions round out the family:
- assertThrows accepts the exception type or any subclass of it.
- assertThrowsExactly rejects a subclass, so only the named type passes.
- assertDoesNotThrow states the opposite expectation, that the block completes cleanly.
The JUnit 4 code on this page still runs on the JUnit Platform through the vintage engine, so nothing above has to be rewritten to keep working while a project migrates.
Common Mistakes When Testing Exceptions in JUnit
Exception tests fail in a small number of recognisable ways. Each row names the symptom, the cause and the fix.
| Symptom | Cause | Fix |
| Expected exception: java.lang.ArithmeticException | The method completed without throwing. | Check that the input really is invalid, then re-run. |
| The test passes but the wrong line threw | The expected parameter watches the whole method, including setup. | Move the setup out, or switch to assertThrows around one call. |
| Unhandled exception type in the editor | A checked exception is thrown but never declared. | Add throws to the test method signature. |
| Test passes on a subclass you did not intend | assertThrows accepts subclasses of the named type. | Use assertThrowsExactly for a strict type match. |
| expected is not a valid attribute | The test was compiled against the Jupiter @Test annotation. | Import org.junit.Test for JUnit 4, or move to assertThrows. |
The last row is the one that catches most people mid-migration, because both annotations are named @Test and only the import tells them apart. Keeping one JUnit version per test case class avoids the whole class of problem.



