How do I assert my exception message with JUnit Test annotation?
Solution 1
You could use the @Rule
annotation with ExpectedException
, like this:
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
expectedEx.expect(RuntimeException.class);
expectedEx.expectMessage("Employee ID is null");
// do something that should throw the exception...
System.out.println("=======Starting Exception process=======");
throw new NullPointerException("Employee ID is null");
}
Note that the example in the ExpectedException
docs is (currently) wrong - there's no public constructor, so you have to use ExpectedException.none()
.
Solution 2
In JUnit 4.13 you can do:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
...
@Test
void exceptionTesting() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> { throw new IllegalArgumentException("a message"); }
);
assertEquals("a message", exception.getMessage());
}
This also works in JUnit 5 but with different imports:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
...
Solution 3
I like the @Rule
answer. However, if for some reason you don't want to use rules. There is a third option.
@Test (expected = RuntimeException.class)
public void myTestMethod()
{
try
{
//Run exception throwing operation here
}
catch(RuntimeException re)
{
String message = "Employee ID is null";
assertEquals(message, re.getMessage());
throw re;
}
fail("Employee Id Null exception did not throw!");
}
Solution 4
Do you have to use @Test(expected=SomeException.class)
? When we have to assert the actual message of the exception, this is what we do.
@Test
public void myTestMethod()
{
try
{
final Integer employeeId = null;
new Employee(employeeId);
fail("Should have thrown SomeException but did not!");
}
catch( final SomeException e )
{
final String msg = "Employee ID is null";
assertEquals(msg, e.getMessage());
}
}
Solution 5
Actually, the best usage is with try/catch. Why? Because you can control the place where you expect the exception.
Consider this example:
@Test (expected = RuntimeException.class)
public void someTest() {
// test preparation
// actual test
}
What if one day the code is modified and test preparation will throw a RuntimeException? In that case actual test is not even tested and even if it doesn't throw any exception the test will pass.
That is why it is much better to use try/catch than to rely on the annotation.
Cshah
I have been working primarily on java technologies in developing management products for smartphones. My work involves working on java, hibernate, axis webservices, maven and ant. I m moving on to learn C# and .NET technologies (Mar 2009) Linkedin:https://www.linkedin.com/in/chethu
Updated on July 12, 2022Comments
-
Cshah almost 2 years
I have written a few JUnit tests with
@Test
annotation. If my test method throws a checked exception and if I want to assert the message along with the exception, is there a way to do so with JUnit@Test
annotation? AFAIK, JUnit 4.7 doesn't provide this feature but does any future versions provide it? I know in .NET you can assert the message and the exception class. Looking for similar feature in the Java world.This is what I want:
@Test (expected = RuntimeException.class, message = "Employee ID is null") public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}
-
Cshah about 14 yearsI m aware of writing a catch block and using assert within that but for better code readability i want to do with annotations.
-
NeplatnyUdaj over 10 yearsAlso you won't get such nice message as when doing it the "right" way.
-
William Price over 9 yearsThe problem with the try/catch version, now that JUnit provides
@Test(expected=...)
andExpectedException
, is that I have seen on numerous occasions someone forgetting to put the call tofail()
at the end of thetry
block. If not caught by code review, your test may be false-positive and always pass. -
redDevil over 8 yearsNote: For me when the
expectMessage
was specified as an empty string, the comparison for the message was not performed -
Bumbolt over 8 yearsUseful to me. Thanks. The testmethod should have
throws RuntimeException
after adding code that throws an exception. Do not catch it... -
Sridhar Sarnobat almost 8 yearsI personally wouldn't want to use this since creating fields for the purpose of a small subset of methods is bad practice. Not a criticism of the response, but of JUnit's design. The OP's hypothetical solution would be so much better if it existed.
-
Sridhar Sarnobat almost 8 yearsSadly, this is my answer too.
-
Egl over 7 yearsUsing Jesse Merriman response, the exception is only checked in test methods that call to expectedEx.expect() and expectedEx.expectMessage(). The other methods will use the definition expectedEx = ExpectedException.none(), that is, no exception expected.
-
luis.espinal over 7 yearsThe concerns regarding code changes are alleviated by having small, permutation-specific test cases. Sometimes it is inevitable and we have to rely in the catch/try method, but if that happens frequently, then chances are that we need to revise the way we write our test case functions.
-
DennisK over 7 yearsThat's a problem with your test and/or code. You do NOT expect a general RuntimeException, you expect a specific exception, or at the very least a specific message.
-
Admin over 7 yearsI used
RuntimeException
as an example, replace this exception with any other exception. -
tuan.dinh over 7 years@redDevil: The expectedMessage checks if the error message "contains" the string specified in this function (like a substring of the error message)
-
Sridhar Sarnobat over 7 yearsThis is why I don't like all this declarative stuff. It makes it difficult to access what you want.
-
Charney Kaye about 7 yearsExcellent solution, for readability. I call mine e.g.
failure.expectMessage(...)
-
Sivabalan about 7 yearsexpectMessage with string parameter does a String.contains checking, for exact match of exception message use hamcrest matcher
failure.expectMessage(CoreMatchers.equalTo(...))
-
WesternGun about 5 yearsLike this solution. Should move to JUnit 5.
-
granadaCoder over 4 yearsGaaaaaaaaa. 4.13 still in beta as of today (Fall, 2019)? mvnrepository.com/artifact/junit/junit
-
aasha over 4 yearsCan someone help me understand why this answer is a -1
-
Huazhe Yin over 4 yearsThe question is asking for
Junit
but ur answer is givingTestNG
-
heroin about 4 years
ExpectedException.none()
is deprecated since Junit 4.13 -
Simon almost 4 yearsv4.13 is not in beta state anymore (release in January 2020)
-
janusz j over 3 years(expected = RuntimeException.class) and throw re; are not neccessary;. There should be just method which throws exception within try and in catch assertions.
-
Lansorian over 3 years@janusz j: I personally prefer to leave the
(expected...
andthrow re;
lines, but remove thefail(...
line. Can you or anyone else please tell me why my preference is/isn't a good practice? -
janusz j over 3 yearswithin try catch, you catch exception wherever you want. When you have for ex: same Exception types throwing in different places you won't get to know where it has been thrown.
-
Lansorian over 3 years@janusz j: Thank you and I see. In other words, if my test method has 0 line of code outside the try catch block, it would be fine?
-
vdimitrov over 3 yearsSince
assertThrows
is available in JUnit 4.13, this should be the accepted answer -
Jules Kerssemakers about 3 yearsI was already using 4.13
assertThrows
, but did not yet know that itreturn
s the exception for subsequent inspection. +1, exactly what I needed :-D -
Pipo over 2 years@heroin should have provided the replacement ;)
-
Pipo over 2 years
ExpectedException.none()
Deprecated. Since 4.13 Assert.assertThrows can be used to verify that your code throws a specific exception. -
saurabh gupta over 2 years@aasha Your answer indeed helped me. Thanks.