Unit-Testing: Exceptions testen [Java 8 Update]

In meinem Beitrag Unit-Testing: Exceptions testen habe ich gezeigt, wie sich Exceptions mit JUnit testen lassen, ohne dabei die Teststruktur verändern zu müssen (Arrange-Act-Assert; Given-When-Then) oder zusätzliche Bibliotheken verwenden zu müssen:

@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;
   }
}

Dank Java 8 und Lambdas lässt sich der Code vereinfachen:

@Test
public void methodThrowsException() {
   Exception ex = tryCall(() -> subjectUnderTest.foo(someIllegalArgument));
   assertThat(ex.getMessage(), is("foobarfoo"));
}

private Exception tryCall(Callable<?> callable) {
   try {
      callable.call();
      return null;
   } catch (Exception ex) {
      return ex;
   }
}

Die Vereinfachung besteht nun darin, dass die Methode tryCall in eine Hilfsklasse ausgelagert werden kann – sie ist jetzt unabhängig vom konkreten Testfall.

Veröffentlicht unter Blog | Verschlagwortet mit

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.

Veröffentlicht unter Blog | Verschlagwortet mit