How do I assert my exception message with JUnit Test annotation?

273,085

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.

Share:
273,085
Cshah
Author by

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, 2022

Comments

  • Cshah
    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
    Cshah about 14 years
    I m aware of writing a catch block and using assert within that but for better code readability i want to do with annotations.
  • NeplatnyUdaj
    NeplatnyUdaj over 10 years
    Also you won't get such nice message as when doing it the "right" way.
  • William Price
    William Price over 9 years
    The problem with the try/catch version, now that JUnit provides @Test(expected=...) and ExpectedException, is that I have seen on numerous occasions someone forgetting to put the call to fail() at the end of the try block. If not caught by code review, your test may be false-positive and always pass.
  • redDevil
    redDevil over 8 years
    Note: For me when the expectMessage was specified as an empty string, the comparison for the message was not performed
  • Bumbolt
    Bumbolt over 8 years
    Useful to me. Thanks. The testmethod should have throws RuntimeException after adding code that throws an exception. Do not catch it...
  • Sridhar Sarnobat
    Sridhar Sarnobat almost 8 years
    I 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
    Sridhar Sarnobat almost 8 years
    Sadly, this is my answer too.
  • Egl
    Egl over 7 years
    Using 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
    luis.espinal over 7 years
    The 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
    DennisK over 7 years
    That'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
    Admin over 7 years
    I used RuntimeException as an example, replace this exception with any other exception.
  • tuan.dinh
    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
    Sridhar Sarnobat over 7 years
    This is why I don't like all this declarative stuff. It makes it difficult to access what you want.
  • Charney Kaye
    Charney Kaye about 7 years
    Excellent solution, for readability. I call mine e.g. failure.expectMessage(...)
  • Sivabalan
    Sivabalan about 7 years
    expectMessage with string parameter does a String.contains checking, for exact match of exception message use hamcrest matcher failure.expectMessage(CoreMatchers.equalTo(...))
  • WesternGun
    WesternGun about 5 years
    Like this solution. Should move to JUnit 5.
  • granadaCoder
    granadaCoder over 4 years
    Gaaaaaaaaa. 4.13 still in beta as of today (Fall, 2019)? mvnrepository.com/artifact/junit/junit
  • aasha
    aasha over 4 years
    Can someone help me understand why this answer is a -1
  • Huazhe Yin
    Huazhe Yin over 4 years
    The question is asking for Junit but ur answer is giving TestNG
  • heroin
    heroin about 4 years
    ExpectedException.none() is deprecated since Junit 4.13
  • Simon
    Simon almost 4 years
    v4.13 is not in beta state anymore (release in January 2020)
  • janusz j
    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
    Lansorian over 3 years
    @janusz j: I personally prefer to leave the (expected... and throw re; lines, but remove the fail(... line. Can you or anyone else please tell me why my preference is/isn't a good practice?
  • janusz j
    janusz j over 3 years
    within 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
    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
    vdimitrov over 3 years
    Since assertThrows is available in JUnit 4.13, this should be the accepted answer
  • Jules Kerssemakers
    Jules Kerssemakers about 3 years
    I was already using 4.13 assertThrows, but did not yet know that it returns the exception for subsequent inspection. +1, exactly what I needed :-D
  • Pipo
    Pipo over 2 years
    @heroin should have provided the replacement ;)
  • Pipo
    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
    saurabh gupta over 2 years
    @aasha Your answer indeed helped me. Thanks.