InvocationTargetException fix

A few years ago I started giving my students unit tests for past AP Computer Science free response questions.  It was pretty tedious to write them, but I’ve got most of them done.

Most of my tests use reflection to some level. In most cases it’s just to set instance variables before running another method so that I know it’s testing the method and not that the instance variables were set correctly. But sometimes I have to use reflection to call a private method, and that causes issues with InvocationTargetException when the student’s code doesn’t work.

An InvocationTargetException is when reflection calls a method and that method throws an exception. Fortunately, there is a getCause() method in the exception that returns the exception thrown by the tested code.

Here’s a method from my unit tests for the RouteCipher free response question on the 2011 exam.

public void test_MeetAtMidnight() throws Exception {
  RouteCipher rc = new RouteCipher( 3, 5 );
  setField( rc, "numRows", 3 );
  setField( rc, "numCols", 5 );
  Method m = RouteCipher.class.getDeclaredMethod( "fillBlock", new Class[]{ String.class } );
  m.setAccessible( true );

  // This is the problem line
  m.invoke( rc, "Meet at midnight" );

  String[][] exp = { { "M", "e", "e", "t", " " }, { "a", "t", " ", "m", "i" }, { "d", "n", "i", "g", "h" } };
  String[][] act = getField( rc, "letterBlock" );

  assertArrayEquals( "fillBlock failed", exp, act );
}

It’s the line starting with m.invoke that we want to talk about.

This line calls a method m from the class rc. Not the easiest way to do it, but it’s necessary when the tested method, fillBlock in this case, is private. That’s also the line that will throw an InvocationTargetException when fillBlock throws an exception.

We’ll fix it with a try / catch.

public void test_MeetAtMidnight() throws Exception {
  RouteCipher rc = new RouteCipher( 3, 5 );
  setField( rc, "numRows", 3 );
  setField( rc, "numCols", 5 );
  Method m = RouteCipher.class.getDeclaredMethod( "fillBlock", new Class[]{ String.class } );
  m.setAccessible( true );
  try {
    m.invoke( rc, "Meet at midnight" );
  } catch ( InvocationTargetException e ) {
    throw (Exception) e.getCause();
  }
  String[][] exp = { { "M", "e", "e", "t", " " }, { "a", "t", " ", "m", "i" }, { "d", "n", "i", "g", "h" } };
  String[][] act = getField( rc, "letterBlock" );

  assertArrayEquals( "fillBlock failed", exp, act );
}

m.invoke still gets called the same way, but if it throws an InvocationTargetException it’s caught. Inside the catch we throw the cause of the exception which works out to be the exception from the student’s code.

One minor thing is that e.getCause() has to be cast as an Exception. It returns a Throwable and the test method throws an Exception.

 

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *