如何测试抛出IllegalStateException的Java私有构造方法?
Great question! Your current test works fine when the constructor doesn’t throw an exception, but we need to adjust it to verify that the expected IllegalStateException is actually being thrown when the private constructor is invoked. Here are two straightforward approaches based on your JUnit version:
Option 1: Using JUnit 5 (Recommended for Modern Projects)
JUnit 5’s assertThrows method is clean and direct—it lets you execute the code that should throw an exception, and verifies the exception type (and even its message) in one step.
Here’s how to modify your test:
@Test public void privateConstructor_ThrowsIllegalStateException() throws Exception { // Retrieve the private constructor Constructor<DetailRecord> constructor = DetailRecord.class.getDeclaredConstructor(); // Verify the constructor is indeed private assertTrue(Modifier.isPrivate(constructor.getModifiers())); // Make the constructor accessible constructor.setAccessible(true); // Assert that invoking the constructor throws the expected exception IllegalStateException thrownException = assertThrows( IllegalStateException.class, constructor::newInstance // Lambda to call the constructor ); // Optional: Verify the exception message matches your expected value assertEquals(ExceptionCodes.FACTORY_CLASS.getMessage(), thrownException.getMessage()); }
How this works:
assertThrowsexecutes the lambda (constructor::newInstance) and catches any exception thrown.- If the exception is of type
IllegalStateException, the test passes; if no exception is thrown, or the wrong type is thrown, the test fails. - The optional
assertEqualsstep confirms the exception message is exactly what you defined, adding an extra layer of validation for your business logic.
Option 2: Using JUnit 4 (For Legacy Projects)
If you’re still using JUnit 4, you can use the ExpectedException rule to declare the exception you expect before invoking the constructor.
Here’s the modified test:
import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; public class DetailRecordTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void privateConstructor_ThrowsIllegalStateException() throws Exception { Constructor<DetailRecord> constructor = DetailRecord.class.getDeclaredConstructor(); assertTrue(Modifier.isPrivate(constructor.getModifiers())); constructor.setAccessible(true); // Declare the expected exception type expectedException.expect(IllegalStateException.class); // Declare the expected exception message expectedException.expectMessage(ExceptionCodes.FACTORY_CLASS.getMessage()); // Invoke the constructor—this will throw the exception, which the rule will catch constructor.newInstance(); } }
How this works:
- The
@Ruleannotation sets upExpectedExceptionto monitor the test method. - We first tell the rule what exception type and message we expect.
- When we call
constructor.newInstance(), the exception is thrown and caught by the rule. If it matches our expectations, the test passes.
Key Takeaway
Both approaches focus on two critical checks:
- Confirming the constructor is private (your original check, which we keep).
- Verifying that invoking the constructor throws the exact
IllegalStateException(with the correct message, if needed) that your implementation defines.
内容的提问来源于stack exchange,提问作者Santiago Molano

