How do you assert that a certain exception is thrown in JUnit 4 tests?

1,615,706

Solution 1

It depends on the JUnit version and what assert libraries you use.

The original answer for JUnit <= 4.12 was:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

Though answer https://stackoverflow.com/a/31826781/2986984 has more options for JUnit <= 4.12.

Reference :

Solution 2

Edit: Now that JUnit 5 and JUnit 4.13 have been released, the best option would be to use Assertions.assertThrows() (for JUnit 5) and Assert.assertThrows() (for JUnit 4.13+). See my other answer for details.

If you haven't migrated to JUnit 5, but can use JUnit 4.7, you can use the ExpectedException Rule:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

This is much better than @Test(expected=IndexOutOfBoundsException.class) because the test will fail if IndexOutOfBoundsException is thrown before foo.doStuff()

See this article for details.

Solution 3

Be careful using expected exception, because it only asserts that the method threw that exception, not a particular line of code in the test.

I tend to use this for testing parameter validation, because such methods are usually very simple, but more complex tests might better be served with:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

Apply judgement.

Solution 4

As answered before, there are many ways of dealing with exceptions in JUnit. But with Java 8 there is another one: using Lambda Expressions. With Lambda Expressions we can achieve a syntax like this:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown accepts a functional interface, whose instances can be created with lambda expressions, method references, or constructor references. assertThrown accepting that interface will expect and be ready to handle an exception.

This is relatively simple yet powerful technique.

Have a look at this blog post describing this technique: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

The source code can be found here: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

Disclosure: I am the author of the blog and the project.

Solution 5

in junit, there are four ways to test exception.

junit5.x

  • for junit5.x, you can use assertThrows as following

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    

junit4.x

  • for junit4.x, use the optional 'expected' attribute of Test annonation

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • for junit4.x, use the ExpectedException rule

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • you also can use the classic try/catch way widely used under junit 3 framework

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • so

    • if you like junit 5, then you should like the 1st one
    • the 2nd way is used when you only want test the type of exception
    • the first and last two are used when you want test exception message further
    • if you use junit 3, then the 4th one is preferred
  • for more info, you can read this document and junit5 user guide for details.

Share:
1,615,706
jbcoe
Author by

jbcoe

...! (contactable via email: domain sdufresne.info, user stefan (i.e user@domain))

Updated on July 08, 2022

Comments

  • jbcoe
    jbcoe almost 2 years

    How can I use JUnit4 idiomatically to test that some code throws an exception?

    While I can certainly do something like this:

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
      boolean thrown = false;
    
      try {
        foo.doStuff();
      } catch (IndexOutOfBoundsException e) {
        thrown = true;
      }
    
      assertTrue(thrown);
    }
    

    I recall that there is an annotation or an Assert.xyz or something that is far less kludgy and far more in-the-spirit of JUnit for these sorts of situations.

    • ZeroOne
      ZeroOne over 11 years
      The problem with any other approach but this is that they invariably end the test once the exception has been thrown. I, on the other hand, often still want to call org.mockito.Mockito.verify with various parameters to make sure that certain things happened (such that a logger service was called with the correct parameters) before the exception was thrown.
    • PhoneixS
      PhoneixS about 10 years
      You can see how to exceptions test in JUnit wiki page github.com/junit-team/junit/wiki/Exception-testing
    • tddmonkey
      tddmonkey over 9 years
      @ZeroOne - For that I would have two different tests- one for the exception and one to verify interaction with your mock.
    • Dilini Rajapaksha
      Dilini Rajapaksha about 7 years
      There is a way to do this with JUnit 5, I have updated my answer below.
    • Hitesh Garg
      Hitesh Garg about 3 years
      Here is a nice example on how assert that an exception is Thrown it in JUnit4 and JUnit5
  • Rodney Gitzel
    Rodney Gitzel over 13 years
    Maybe I'm old school but I still prefer this. It also gives me a place to test the exception itself: sometimes I have exceptions with getters for certain values, or I might simply look for a particular value in the message (e.g. looking for "xyz" in the message "unrecognized code 'xyz'").
  • Eddie
    Eddie about 13 years
    I think NamshubWriter's approach gives you the best of both worlds.
  • OCB
    OCB almost 13 years
    This piece of code will not work if you expect an exception only somewhere in your code, and not a blanket like this one.
  • OCB
    OCB almost 13 years
    +1 useful in some scenarios where expected = xx doesn't match requirements.
  • Artem Oboturov
    Artem Oboturov about 12 years
    @skaffman This wouldn't work with org.junit.experimental.theories.Theory runned by org.junit.experimental.theories.Theories
  • Rachel
    Rachel almost 12 years
    @skaffman: Is there a way to do similar thing in junit 3.8, expect method to throw exception?
  • bacar
    bacar almost 12 years
    @skaffman - If I've understood this correctly, it looks like the exception.expect is being applied only within one test, not the whole class.
  • user1154664
    user1154664 over 11 years
    Using ExpectedException you could call N exception.expect per method to test like this exception.expect(IndexOutOfBoundsException.class); foo.doStuff1(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff2(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff3();
  • jontejj
    jontejj about 11 years
    Also, you won't see what kind of Exception ex is in the test results when the day comes where the test fails.
  • Mohammad Jafar Mashhadi
    Mohammad Jafar Mashhadi almost 11 years
    If the exception we expect to be thrown is an checked exception, should we add throws or try-catch or test this situation in another way?
  • TmTron
    TmTron over 10 years
    IMHO the answer from rwoo below is a much better solution (code.google.com/p/catch-exception) i.e. in this example when you throw new NullPointerException(); after foo.doStuff() the test will not fail with an NPE.
  • Jason Thompson
    Jason Thompson over 10 years
    @MartinTrummer No code should run after foo.doStuff() since the exception is thrown and the method is exited. Having code after an expected exception (with the exception of closing resources in a finally) is unhelpful anyway since it should never be executed if the exception is thrown.
  • Jason Thompson
    Jason Thompson over 10 years
    I thought about doing something like this as well, but ultimately discovered that the true power of ExpectedException is that not only can you specify the expected exception, but you can also specify certain properties of the exception such as the expected cause or expected message.
  • TmTron
    TmTron over 10 years
    @Jason Thompson: "should" is nice, but not bullet-proof: With verify-exception you can be sure that the test is okay, even if someone else added some code after doStuff(): when the new code passes, so does the test and more importantly: when the new code throws, the test will fail. the given "solution" would hide this fact (and moreover verify-exception is more concise and thus less error-prone):
  • Jason Thompson
    Jason Thompson over 10 years
    @MartinTrummer What I'm struggling to understand is how code can execute after an exception is thrown. Perhaps you can give an example. My understanding is that as soon as an exception is thrown, the method exits with an exception. So if I had a function that looked like this: void myMethod { throw new IllegalStateException(); executeOtherMethod(); } As far as I know, there is no way executeOtherMethod() would ever be called. So it's a non issue. This code doesn't change how java works, but rather tells the test runner that the method will throw an exception and some characteristics of it.
  • TmTron
    TmTron over 10 years
    @Jason but that's not the code you have. you have foo.doStuff(); bar.doStuff(); - and you expect foo.doStuff(); to throw. so if it does throw, bar.doStuff() is never executed (that's bad). but even worse is the case, when the code changes and foo.doStuff() does not throw anymore. now it's possible that bar.doStuff() throws insteead- the test passes - and you don't notice that the new code actually broke the original expectations that foo.doStuff(); throws.
  • NamshubWriter
    NamshubWriter about 10 years
    @user1154664 Actually, you can't. Using ExpectedException you can only test that one method throws an exception, because when that method is called, the test will stop executing because it threw the expected exception!
  • Tom
    Tom almost 10 years
    My guess is that this solution has some of the same drawbacks as mocks? For example, if foo is final it will fail because you can't proxy foo?
  • Tom
    Tom almost 10 years
    @JasonThompson: usually in a testing environment the exception is swallowed and you assert it was thrown. The test doesn't exit in that case.
  • Tom
    Tom almost 10 years
    As I commented on rwoo's answer, I think the drawback with his library is that you can't proxy every object [for example classes that are final?]... but I'm not sure without trying it out.
  • Dawood ibn Kareem
    Dawood ibn Kareem almost 10 years
    Your first sentence just isn't true. When using ExpectedException, the normal thing to do is to set the expectation immediately before the line that you expect to throw the exception. That way, if an earlier line throws the exception, it won't trigger the rule, and the test will fail.
  • Dawood ibn Kareem
    Dawood ibn Kareem almost 10 years
    This is the best approach. There are two advantages here, compared to skaffman's solution. Firstly, the ExpectedException class has ways of matching the exception's message, or even writing your own matcher that depends on the class of exception. Secondly, you can set your expectation immediately before the line of code that you expect to throw the exception - which means your test will fail if the wrong line of code throws the exception; whereas there's no way to do that with skaffman's solution.
  • singe3
    singe3 almost 10 years
    Doesn't work when we extend TestCase, the test fails although the exception is thrown as expected
  • NamshubWriter
    NamshubWriter over 9 years
    @singe31 The OP asked about JUnit4, and JUnit4-style tests shouldn't extend TestCase
  • grackkle
    grackkle over 9 years
    Interesting, but doesn't fit to AAA (Arrange Act Assert), where you want to do the Act and the Assert step in actually different steps.
  • mkobit
    mkobit over 9 years
    @DavidWallace the problem I with this approach is if you want to assert or check any behavior after the executed methods (like verify that any mock behavior did or did not occur) you cannot do it because your test will exit execution when the exception is throw. This can lead to unexpected test behavior if you have some verify after the method call that you believe is getting executed, but it actually isn't.
  • Dawood ibn Kareem
    Dawood ibn Kareem over 9 years
    @MikeKobit - So are you testing that the exception is thrown, or are you testing something else? I am a strong advocate of the "one test case, one assertion" approach - that is, if you're testing the throwing of the exception, there shouldn't be any other asserts or verifies occurring after the exception.
  • mkobit
    mkobit over 9 years
    @DavidWallace I've had the case before where I want to test a method on a component that will throw an exception. I also want to make sure that a side effect has/has not happened. If I am using a mock framework like Mockito with the answer above, I wouldn't be able to verify any behavior. Example demoing sort of what I am talking about - stackoverflow.com/questions/13224288/…
  • Dawood ibn Kareem
    Dawood ibn Kareem over 9 years
    @MikeKobit OK, fair enough, that sounds reasonable. I imagine that would be a fairly rare kind of test case though.
  • NamshubWriter
    NamshubWriter over 9 years
    In JUnit tests, it's better to use Assert.fail(), not assert, just in case your tests run in an environment where assertions are not enabled.
  • rwitzel
    rwitzel over 9 years
    Tom, if doStuff() is part of an interface the proxy approach will work. Otherwise this approach will fail, you are right.
  • Kevin Wittek
    Kevin Wittek over 9 years
    Roy Osherove discourages this kind of Exception testing in The art of Unit Testing, since the Exception might be anywhere inside the test and not only inside the unit under test.
  • Hakanai
    Hakanai over 9 years
    @bln-tom Technically it is two different steps, they're just not in that order. ;p
  • Selwyn
    Selwyn about 9 years
    I like this solution but can I download this from a maven repo?
  • Thomas
    Thomas about 9 years
    It looks like Eclipse's coverage tool has trouble with this (not that that really matters)
  • NamshubWriter
    NamshubWriter about 9 years
    @Airduster one implementation of this idea that's available on Maven is stefanbirkner.github.io/vallado
  • NamshubWriter
    NamshubWriter about 9 years
    @MJafarMash if the exception you expect to throw is checked, then you would add that exception to the throws clause of the test method. You do the same any time you are testing a method that is declared to throw a checked exception, even if the exception isn't triggered in the particular test case.
  • NamshubWriter
    NamshubWriter almost 9 years
    @CristianoFontes a simpler version of this API is slated for JUnit 4.13. See github.com/junit-team/junit/commit/…
  • NamshubWriter
    NamshubWriter over 8 years
    The second catch would swallow the stack trace if some other exception is thrown, losing useful information
  • Anmol Gupta
    Anmol Gupta over 8 years
    This was apt for my case. I wanted to check state of some objects after the exception was thrown which could not be done with 'expected' as it just exists after exception is thrown.
  • ycomp
    ycomp about 8 years
    most concise way and nobody appreciates it, strange.. I only have one problem with the assertJ library, assertThat conflicts name-wise with junit's. more about assertJ throwby: JUnit: Testing Exceptions with Java 8 and AssertJ 3.0.0 ~ Codeleak.pl
  • sara
    sara about 8 years
    agree with @Kiview's point, this way might not actually test what you wanted to test. false positives are a pain.
  • weston
    weston about 8 years
    @ycomp Well it is a new answer on a very old question, so the score difference is deceptive.
  • Pierre Henry
    Pierre Henry about 8 years
    That's probably the best solution if one can use Java 8 and AssertJ !
  • nickbdyer
    nickbdyer about 8 years
    I disagree with @Kiview/Roy Osherove. In my view, tests should be for behaviour, not implementation. By testing that a specific method can throw an error, you are tying your tests directly to the implementation. I would argue that testing in the method shown above provides a more valuable test. The caveat I would add is that in this case I would test for a custom exception, so that I know I am getting the exception I really want.
  • Kevin Wittek
    Kevin Wittek about 8 years
    @nickbdyer Of course you want to test the behaviour. But do you want to test the behaviour of ArrayList? constructor or of the get() method? In the example above, you are testing both. The test would pass if your constructor would throw this exception, although you want to test the get() method.
  • nickbdyer
    nickbdyer about 8 years
    Neither. I want to test the behaviour of the class. What is important, is that if I try to retrieve something that isn't there, I get an exception. The fact that the data structure is ArrayList that responds to get() is irrelevant. If I chose in the future to move to a primitive array, I would then have to change this test implementation. The data structure should be hidden, so that the test can focus on the behaviour of the class.
  • Nicolas Cornette
    Nicolas Cornette almost 8 years
    For me this is the best answer, it covers all the ways very clearly, thanks ! Personally I continue using the 3rd option even with Junit4 for readability, to avoid empty catch block you can also catch Throwable and assert type of e
  • NamshubWriter
    NamshubWriter almost 8 years
    The second catch would swallow the stack trace if some other exception is thrown, losing useful information
  • Arthur Welsch
    Arthur Welsch over 7 years
    And also this is compatible with Arquillian-Tests. I tried this with @Rule Annotation but got Initialization Error with Arquillian.
  • mike rodent
    mike rodent over 7 years
    @ycomp I suspect this name conflict may be by design: the AssertJ library therefore strongly encourages you never to use the JUnit assertThat, always the AssertJ one. Also the JUnit method returns only a "regular" type, whereas the AssertJ method returns an AbstractAssert subclass ... allowing stringing of methods as above (or whatever the technical terms is for this...).
  • mike rodent
    mike rodent over 7 years
    @weston actually I've just used your technique in AssertJ 2.0.0. No excuse for not upgrading, no doubt, but though you might like to know.
  • weston
    weston over 7 years
    @mikerodent "allowing stringing of methods as above (or whatever the technical terms is for this...)" fluent would be the term. So, "... allowing fluent statements."
  • weston
    weston over 7 years
    @mikerodent that is interesting that it works in 2, I don't know why I thought was 3
  • Ungeheuer
    Ungeheuer over 7 years
    @DavidWallace how does ExpectedException test for exception message? I dont see anything in the 4.12 API for that.
  • Dawood ibn Kareem
    Dawood ibn Kareem over 7 years
    You can pass a Hamcrest Matcher to expect to analyse the exception. Or even easier - you can pass a String to expectMessage that you expect to be a substring of the message. junit.org/junit4/javadoc/4.12/org/junit/rules/…
  • Halley
    Halley over 7 years
    This question pops up in Google when you search for JUnit Exceptions. Can someone edit this answer and provide the info related to JUnit 4.7's features? stackoverflow.com/a/16725280/1199832
  • miuser
    miuser about 7 years
    Is it possible to use ExpectedException to expect checked exception?
  • Dylanthepiguy
    Dylanthepiguy about 7 years
    @OhChinBoon You could make your own subclass of Exception or RuntimeException and catch that. And then make sure your subclass isn't being used in more than one place
  • Andy
    Andy about 7 years
    @RafalBorowiec technically, new DummyService()::someMethod is a MethodHandle, but this approach works equally well with lambda expressions.
  • Albert Gan
    Albert Gan almost 7 years
    Furthermore, with this approach, we could add more assertions in the exception handling for state checks if necessary
  • anre
    anre almost 7 years
    Adding org.junit.jupiter:junit-jupiter-engine:5.0.0-RC2 dependency (in addition to the already existing junit:junit:4.12) to be able to use assertThrows is perhaps not the preferred solution, but did not cause any issues for me.
  • NoName
    NoName over 6 years
    public void testIndexOutOfBoundsException() throws IndexOutOfBoundsException { ... } should be added.
  • Daniel Alder
    Daniel Alder over 6 years
    befor the line with } catch (, you should insert fail("no exception thrown");
  • Vadzim
    Vadzim over 6 years
    @NamshubWriter, it seems that junit 4.13 was abandoned in favor of junit 5: stackoverflow.com/questions/156503/…
  • NamshubWriter
    NamshubWriter over 6 years
    @Vadzim JUnit 4.13 is not abandoned. The team realizes that some people cannot migrate to JUnit 5 and 4.13 has some nice fixes, but most of the recent efforts have been focused on the 5.x code base.
  • Juan Salvador
    Juan Salvador over 6 years
    This works nice !.... but, it seems that when you use this sort of solutions, actually you are not doing the right thing with exceptions. Please read the link the new solution3. Errors and Exceptions should not interrupt the unit testing.
  • benez
    benez over 6 years
    i think this way is legit as well, just a personal preference. but it is also nice to mention that this might "catch" all exceptions of that type within your test and sometimes it will be more accurate to catch the exception inside the test around a specific method call, where you expect the exception to occur. so if you keep that in mind, it's legit to write a test just like this
  • Marc Bouvier
    Marc Bouvier about 6 years
    You can do it that way : exception.expectMessage("foo bar");
  • Marc Bouvier
    Marc Bouvier about 6 years
    I agree, ExpectedException rule is not suitable for every situation.
  • Paul Samsotha
    Paul Samsotha about 6 years
    All it is is an accumulation of the top three answers. IMO, this answer shouldn't even have been posted if it is not adding nothing new. Just answering (a popular question) for the rep. Pretty useless.
  • walsh
    walsh almost 6 years
    sure, because you can pass any type derived from Trowable to the method ExpectedException.expect. please see it's signature. @miuser
  • Vivek Chavda
    Vivek Chavda almost 6 years
    @nickbdyer I think there's some miscommunication here. Kiview/Kevin Wittek is saying any method used in the test could throw the exception and cause the test to pass without reaching the statement that is actually supposed to throw an exception of that type. The data structure being used is irrelevant to his point. If the structure is wrapped inside a method of the class under test, you're right that it doesn't matter which statement inside that class is throwing the exception, but he wasn't contradicting that.
  • BitfulByte
    BitfulByte almost 6 years
    I'm a fan of using the ExpectedException rule but it always bothered me that it breaks with AAA. You've wrote an excellent article to describe all the different approaches and you've definitely encouraged me to try AssertJ :-) Thanks!
  • Brice
    Brice almost 6 years
    @PimHazebroek thanks. AssertJ API is quite rich. Better in my opinion that what JUnit proposes out of the box.
  • hellow
    hellow almost 6 years
    You are answering on a 10 year old question, please expand your answer, why you think it provides something new, that the accepted answer does not cover. Also always try to tell something about your code and provide documentation or explanation.
  • Hulk
    Hulk almost 6 years
    This is basically a duplicate of this 10 year old answer (except for the check for the error message).
  • Hossam Badri
    Hossam Badri almost 6 years
    I don't know why to downvote. There is no duplication. Please have a look at the check of the error msg. This is the right answer that I used today with my team @hellow
  • hellow
    hellow almost 6 years
    It's not always the same person who complains about your question/answer and downvots you (like in this case, I didn't, but please take my words and expand your answer).
  • Nithin
    Nithin almost 6 years
    I am not sure if the test depending just on the error message is a good practice.
  • Hossam Badri
    Hossam Badri almost 6 years
    Yeah it is a good practice and useful when you have many error messages, so that you make sure that this specific one will be thrown (many possible exceptions)
  • Hossam Badri
    Hossam Badri almost 6 years
    Please what do you mean by expand answer Hebrow, this answer is for the question, so if you understand well the question and the code on it so the answer will be straightforward, he is needing just some code
  • Cypher
    Cypher over 5 years
    This can be improved a bit by changing how you assert at the end. assertEquals(ExpectedException.class, e.getClass()) will show you the expected and actual values when the test fails.
  • Clockwork
    Clockwork about 5 years
    This approach is clean, but I don't see how this allows our test to follow "Arrange-Act-Assert", since we have to wrap the "Act" part in an "assertThrow", which is an assert.
  • NamshubWriter
    NamshubWriter about 5 years
    @Clockwork The lambda is the "act". The goal of Arrange-Act-Assert is to make the code clean and simple (and therefore easy to understand and maintain). As you stated, this approach is clean.
  • Clockwork
    Clockwork about 5 years
    I was still hoping I could assert the throw and the exception on the end of the test though, in the "assert" part. In this approach, you need to wrap the act in a first assert to catch it first.
  • NamshubWriter
    NamshubWriter about 5 years
    That would require more code in every test to do the assertion. That's more code and would be error-prone.
  • ebwb
    ebwb about 5 years
    Still using this in 2019. Makes it easy for me to run other verification code after the (otherwise execution-ending-) exception is thrown.
  • tkruse
    tkruse about 4 years
    Junit 4.13 is out, also a version of this also exists with assertJ and google-truth, as indicated by other answers here.
  • dhalfageme
    dhalfageme about 4 years
    How do you set an error message ofr the test if exception is not thrown?
  • Ilya Serbis
    Ilya Serbis about 4 years
    expectThrows() is a part TestNG, not a JUnit
  • avijendr
    avijendr almost 4 years
    This is the best answer
  • JohnnyB
    JohnnyB over 3 years
    Problem with that approach is code coverage will never pick up that line of code
  • granadaCoder
    granadaCoder over 3 years
    I would point out that Junit 4.13 (emphasis on the ".13")...supports assertThrows. I am upvoting, because this is a good "complete overview" answer, but I hope you will consider a 4.13 caveat.
  • SudhirKumar
    SudhirKumar over 2 years
    i have a method execute() which throws checked IOException, going with above approach, catch block didn't catch IOException.