2016-04-27 9 views
2

Ich habe meine Ausnahmen immer mit Anmerkungen getestet.Sollte ich immer Lambda Expressions für Ausnahmetests verwenden?

@Test (expected = Exception.class) 
public void test1() { 
    methodToTest() // throws an exception 
} 

Ich wechselte schließlich zu Java 8, und ich kam in Lambda-Ausdrücke. Jetzt gibt es eine weitere Option, um das gewünschte Ergebnis zu erhalten.

@Test 
public void test2() { 
    assertThrows(Exception.class,() -> methodToTest()); 
} 

public static <X extends Throwable> Throwable assertThrows(
    final Class<X> exceptionClass, final Runnable block) { 
    try { 
     block.run(); 
    } catch(Throwable ex) { 
     if (exceptionClass.isInstance(ex)) 
     return ex; 
    } 
    fail("Failed to throw expected exception"); 
    return null; 
    } 

Ich verstehe, dass mit der zweiten Version, die Sie genauer für einzelne Methoden überprüfen, und Sie müssen sich keine Gedanken über andere Methoden in einem einzigen Test sorgen, dass die erwartete Ausnahme auch werfen könnte. Außerdem können alle Tests bei einer "assertThrows" -Methode die gleiche Struktur haben, weil es auf einen Aufruf für eine Assertion ankommt.

Neben diesen beiden Punkten, gibt es irgendwelche Argumente für den neuen Weg? Für mich fühlt es sich immer noch besser an, mit den Anmerkungen zu gehen, solange ich nur eine einzige Methode innerhalb eines einzigen Tests teste.

+0

Wo die Lambda-Ausdrücke in dieser sind Frage? – Dici

+0

"test2" übergibt einen Lambda-Ausdruck an die assertThrows-Methode, die als Runnable behandelt wird. – leimooo

+1

Fragen Sie, warum es notwendig ist, einen Lambda-Ausdruck zu verwenden?Oder warum Sie all dies tun würden, anstatt die @Test-Annotation (expected = Exception.class) zu verwenden? Wenn es das letztere ist, haben Sie es selbst ziemlich beantwortet, das sind 2 gute Punkte. –

Antwort

3

Sie verfehlten einen dritten Weg brauchen würde, die ExpectedException jUnit Regel:

public class SimpleExpectedExceptionTest { 
    @Rule 
    public ExpectedException thrown= ExpectedException.none(); 

    @Test 
    public void myMethod_throws_no_exception_when_passed_greeting() { 
     fixture.myMethod("hello"); 
    } 

    @Test 
    public void myMethod_throws_MyException_when_passed_farewell() { 
     thrown.expect(MyException.class); 
     fixture.myMethod("goodbye"); 
    } 
} 

Das finde ich klarer als die @Test (expected = ...) Version, da die Die Erwartung nähert sich dem Methodenaufruf.

Es gibt auch die einfache alte Java-Version, die wir mit machen zu tun pflegen:

try { 
    fixture.myMethod("should throw"); 
    fail("Expected an exception"); 
} catch (MyException e) { 
    // expected 
} 

Welche der vielen „besser“ ist, hängt ganz von Kontext. Nehmen Sie nicht universal an. Wählen Sie diejenige, die Ihnen in einer gegebenen Situation den klarsten Test gibt.

Wenn Sie mit der Programmierung von Nicht-Test-Code in einem lambda-zentrischen Stil beginnen, ist es wahrscheinlich, dass Sie den lambda-centric assertThrows() verwenden möchten.

+0

Mit dieser Methode können Sie nach dem Aufruf der Methode Dinge tun, die eine Ausnahme werfen? – Dici

+0

Mit der ersten Methode können Sie beliebigen Code, den Sie mögen, in die Wurftechnik einfügen, aber er wird nicht ausgeführt, wenn die Methode wie erwartet ausgibt. Mit der einfachen alten Java-Version können Sie natürlich alles, was Sie mögen, in den catch-Block setzen, wie Behauptungen über die Ausnahme oder nach dem catch-Block, wie Behauptungen über den Zustand anderer Dinge danach. – slim

+0

Das habe ich mir gedacht, was bringt es dann im Vergleich zu einer Annotation? Es ist ausführlicher und macht dasselbe. Nicht kritisieren, nur fragen. – Dici

2

Wenn Sie nur testen möchten, dass eine Ausnahme ausgelöst wird, ist die erste Syntax besser. Es ist Standard, es ist prägnant, und es hindert Sie daran, den gleichen hässlichen Versuch-Fang immer wieder zu schreiben.

Sie könnten einen etwas komplizierteren Test haben, bei dem Sie bestätigen möchten, dass eine Ausnahme ausgelöst wird und eine Methode wird nicht aufgerufen. In diesem Fall ist das manuelle Abfangen der Ausnahme sinnvoll.

@Test 
public void test1() { 
    DBClient dbClient = spy(new DBClient()); 
    try { 
     new Server().doRequest(new InvalidRequest(), dbClient); 
     fail("should have thrown"); 
    } catch (InvalidRequestException e) { 
     verify(dbClient, times(0)).query(any(Query.class)); 
    } 
} 

In Bezug auf die Verwendung auf Lambda genauer gesagt, es liegt an Ihnen. Nur beachten Sie, dass Runnable nicht geprüfte Ausnahmen werfen können, so dass Sie so etwas wie

@FunctionalInterface 
public interface ThrowingRunnable<E extends Exception> { 
    void run() throws E; 
} 
+1

Randnotiz: mit einem booleschen 'hasThrown' und einer anderen Behauptung darauf ist bonkers. Setzen Sie einfach 'fail() sollte nach Ihrer' neuen Server (...) 'Linie geworfen haben und Sie sind fertig. – GhostCat

+0

Wahr, ich werde bearbeiten – Dici

1

Ich sehe kein Problem mit beiden Ansatz - es gibt keine Richtig oder Falsch in Sachen Stil; benutze einfach den, der am besten zu jeder Situation passt. Ich schlage vor, dass assertThrows sollte auch für geworfen Ausnahmen überprüfen, die nicht dem erwarteten Typ sind, und wie @Dici vermuten lässt, dass eine funktionelle Schnittstelle, werden Ausnahmen verwendet geprüft erlaubt:

public static <X extends Throwable> Throwable assertThrows(
     final Class<X> exceptionClass, final CheckedRunnable block) { 
    try { 
     block.run(); 
    } catch(Throwable ex) { 
     if (exceptionClass.isInstance(ex)) { 
      return ex; 
     } else { 
      throw new AssertionError("Unexpected exception was thrown", ex); 
     } 
    } 
    fail("Failed to throw expected exception"); 
} 

@FunctionalInterface 
public interface CheckedRunnable<R extends RuntimeException> { 
    void run() throws Exception; 
} 
Verwandte Themen