Unit-Testing: Exceptions testen

Möchte man das auftreten von Exceptions mit JUnit testen, so gibt es verschiedene Wege und Möglichkeiten. Die bekanntesten sind sicherlich

@Test(expected=IllegalArgumentException.class)
public void methodThrowsException() {
   subjectUnderTest.foo(someIllegalArgument);
}

und

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void methodThrowsException() {  
   thrown.expect(IllegalArgumentException.class);
   subjectUnderTest.foo(someIllegalArgument);
}

Beide Varianten haben den Nachteil, dass die Given-When-Then (oder Arrange-Act-Assert) Reihenfolge aufgebrochen wird, so dass man häufig „von Hand“ geschriebenen Code wie den folgenden findet:

@Test
public void methodThrowsException() {
   try {
      subjectUnderTest.foo(someIllegalArgument);
      fail("IllegalArgumentException expected, but not thrown!");
   } catch (IllegalArgumentException ex) {
      assertThat(ex.getMessage(), is("foobarfoo"));
   }
}

Jetzt sind die Anweisungen zwar wieder in der Given-When-Then Reihenfolge, aber die try-catch-Anweisungen machen den Code unübersichtlich.

Die folgende Lösung ist uns in den Sinn gekommen, nachdem wir uns im aktuellen Projekt über Clean Code ausgetauscht haben:

@Test
public void methodThrowsException() {
   IllegalArgumentException ex = foo(someIllegalArgument);
   assertThat(ex.getMessage(), is("foobarfoo"));
}

private Exception foo(FooBarFoo fooBarFoo) {
   try {
      subjectUnderTest.foo(someIllegalArgument);
      return null;
   } catch (IllegalArgumentException ex) {
      return ex;
   }
}

Der Testcode ist sauber, die Reihenfolge der Testanweisungen stimmt und es sind keine Erweiterungen wie @Rules oder catch-excpetion notwendig.