Scott's profileSoftware Development Wit...BlogLists Tools Help

Blog


    December 22

    Testing Expected Exceptions with JUnit

    It's always important to unit-test your exceptional cases.  The JUnit 4 @Test annotation allows you to specify an "expected" attribute which is the class of an Exception that you expect to be thrown by a test.  Consider this simple class:

     

    public class MathStuff {

      public int divide(int x, int y)
      {
        if( y == 0 )
        {
          throw new IllegalArgumentException("You can't divide by 0");
        }
       
        return x / y;
      }
    }

    The quick-and-dirty way to test this using the "expected" attribute goes like this:

     

    import static org.junit.Assert.*;

    import org.junit.Before;
    import org.junit.Test;


    public class TestMathStuff {

      private MathStuff math;
     
      @Before
      public void setUp()
      {
        math = new MathStuff();
      }
     
      @Test(expected=IllegalArgumentException.class)
      public void testDivide() {
        @SuppressWarnings("unused")
        int unused = math.divide(5, 0);
      }
    }

    The problem with this is that you usually want to test the exception message, too, and the @Test(expected) functionality provides no way to do this.  Thus, you usually end up writing some boilerplate code like the following:

     

      @Test
      public void testDivideBetter()
      {
        try
        {
          @SuppressWarnings("unused")
          int unused = math.divide(5,0);
        }
        catch(Exception ex)
        {
          assertEquals(IllegalArgumentException.class.getName(), ex.getClass().getName());
          assertEquals("You can't divide by 0", ex.getMessage());
          return;
        }

        fail("Didn't find expected exception of type IllegalArgumentException");
      }

    Obviously, this is a more thorough test.  But repeatedly writing the "try/catch-assert-return/fail" idiom is less than ideal and a hindrance to consistently writing this style of test.

    So today I started looking for a way to wrap this structure up into a helper method.  Here is what I came up with:

     

    import static org.junit.Assert.*;

    public class UnitTestHelper {

      public interface TestBody
      {
        public void run();
      }
     
      /**
       * Run a test body expecting an exception of the
       * given class and with the given message.
       * @param test
       * @param exceptionClass
       * @param message
       */
      public static void expectException(TestBody test,
                       Class<?
    extends Throwable> exceptionClass,
                       String message
    )
      {
        try
        {
          test.run();
        }
        catch(Exception ex)
        {
          assertEquals(exceptionClass.getName(), ex.getClass().getName());
          assertEquals(message, ex.getMessage());
          return;
        }
       
        fail("Didn't find expected exception of type " + exceptionClass.getName());
      }
    }

    To use this, all you have to do is write your test inside the "run" method of an anonymous implementation of TestBody.  Then the helper method takes care of wrapping the test code in the proper exception-handling structure and asserting the exception class and message.  Here is an example:

     

      @Test
      public void testDivideBest()
      {
        UnitTestHelper.expectException(
            new UnitTestHelper.TestBody()
            {
              public void run()
              {
                @SuppressWarnings("unused")
                int unused = math.divide(5, 0);
              }
            },
            IllegalArgumentException.class,
            "You can't divide by 0"
        );
      }

    Personally, I think I'm going to like this approach better.

    Comments (2)

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.
    Scott McMaster has turned off comments on this page.
    Hi Ted.  Multiple versions of verification is a good idea.  The reason I didn't use the Runnable interface is because Runnable has well-known semantics related to threading.  It doesn't seem appropriate to me to overload it for what is basically a closure.
    Feb. 15
    Picture of Anonymous
    Ted M. Young wrote:
    I've done a similar thing, thought I put the fail() inside of the try{} block so I don't need a return in the catch block. Also, I just use the Runnable interface since it already has the public void run() method in it -- was there a reason you created a new Interface? Finally, I have a few variants of this method: one that doesn't care about the exception message, one that matches the message exactly, and one that takes a substring -- sometimes I only care that the message contains a word or two, but not that the whole message matches exactly.
     
    Feb. 13

    Trackbacks

    The trackback URL for this entry is:
    http://softwaredevscott.spaces.live.com/blog/cns!1A9E939F7373F3B7!524.trak
    Weblogs that reference this entry
    • None