it-swarm.com.de

Wie behaupten Sie, dass in JUnit 4-Tests eine bestimmte Ausnahme ausgelöst wird?

Wie kann ich JUnit4 idiomatisch verwenden, um zu testen, ob ein Code eine Ausnahme auslöst?

Während ich so etwas sicher machen kann:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

Ich erinnere mich, dass es eine Annotation oder eine Assert.xyz oder etwas gibt, die für diese Art von Situationen weit weniger klug und weit mehr im Geiste von JUnit sind.

1883
SCdF

JUnit 4 unterstützt dies:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

Referenz: https://junit.org/junit4/faq.html#atests_7

2253
skaffman

Bearbeiten Nach der Veröffentlichung von JUnit5 ist die beste Option, Assertions.assertThrows() zu verwenden (siehe meine andere Antwort ). .

Wenn Sie nicht zu JUnit 5 migriert haben, aber JUnit 4.7 verwenden können, können Sie die Regel ExpectedException verwenden:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

Dies ist viel besser als @Test(expected=IndexOutOfBoundsException.class), da der Test fehlschlägt, wenn IndexOutOfBoundsException vor foo.doStuff() geworfen wird.

Siehe dieser Artikel für Details

1266
NamshubWriter

Seien Sie vorsichtig bei der Verwendung der erwarteten Ausnahme, da nur behauptet wird, dass die Methode diese Ausnahme ausgelöst hat, nicht ein bestimmte Codezeile im Test.

Ich neige dazu, dies zur Validierung von Testparametern zu verwenden, da solche Methoden normalerweise sehr einfach sind, komplexere Tests jedoch besser bedient werden können mit:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

Urteil anwenden.

456
daveb

Wie bereits beantwortet, gibt es in JUnit viele Möglichkeiten, mit Ausnahmen umzugehen. Aber mit Java 8 gibt es noch eine andere: die Verwendung von Lambda-Ausdrücken. Mit Lambda Expressions können wir eine Syntax wie diese erreichen:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown akzeptiert eine funktionale Schnittstelle, deren Instanzen mit Lambda-Ausdrücken, Methodenreferenzen oder Konstruktorreferenzen erstellt werden können. assertThrown, das diese Schnittstelle akzeptiert, erwartet eine Ausnahme und ist bereit, diese zu behandeln.

Dies ist eine relativ einfache und dennoch leistungsstarke Technik.

Schauen Sie sich diesen Blog-Beitrag an, der diese Technik beschreibt: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-Java-8-and-lambda-expressions.html =

Den Quellcode finden Sie hier: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/Java/com/github/kolorobot/exceptions/Java8

Offenlegung: Ich bin der Autor des Blogs und des Projekts.

206
Rafal Borowiec

in junit gibt es vier Möglichkeiten, eine Ausnahme zu testen.

junit5.x

  • für junit5.x können Sie assertThrows wie folgt verwenden

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    

junit4.x

  • verwenden Sie für junit4.x das optionale Attribut 'Expected' von Test Annonation

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • verwenden Sie für junit4.x die ExpectedException-Regel

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • sie können auch den klassischen Try/Catch-Weg verwenden, der im Rahmen von Junit 3 weit verbreitet ist

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • damit

    • wenn du junit 5 magst, dann solltest du das 1. mögen
    • die zweite Methode wird verwendet, wenn Sie nur den Ausnahmetyp testen möchten
    • die ersten beiden werden verwendet, wenn Sie eine weitere Testausnahmemeldung erhalten möchten
    • wenn Sie Junit 3 verwenden, wird das vierte bevorzugt
  • weitere Informationen finden Sie in den Abschnitten dieses Dokument und junit5-Benutzerhandbuch .

127
walsh

tl; dr

  • pre-JDK8: Ich werde den alten guten try-catch-Block empfehlen. (Vergessen Sie nicht, vor dem catch-Blockeine fail() -Angabe einzufügen.)

  • post-JDK8: Verwenden Sie AssertJ oder benutzerdefinierte Lambdas, umexceptionbehaviour zu bestätigen.

Unabhängig von Junit 4 oder JUnit 5.

die lange Geschichte

Es ist möglich, selbst einendo it yourselftry-catch -Block zu schreiben oder die JUnit-Tools (@Test(expected = ...) oder die Funktion @Rule ExpectedException JUnit Rule) zu verwenden. .

Aber diese Wege sind nicht so elegant und lassen sich nicht gutlesbarmit anderen Werkzeugen kombinieren. Darüber hinaus hat JUnit Tooling einige Tücken.

  1. Mit dem try-catch-Block müssen Sie den Block um das getestete Verhalten herum schreiben und die Behauptung in den catch-Block schreiben. Das mag in Ordnung sein, aber viele finden, dass dieser Stil den Lesefluss eines Tests unterbricht. Außerdem müssen Sie am Ende des try-Blocks einen Assert.fail schreiben, da der Test sonst möglicherweise eine Seite der Behauptungen übersieht.PMD,findbugsoderSonarwill solche Probleme erkennen.

  2. Die Funktion @Test(expected = ...) ist interessant, da Sie weniger Code schreiben können und das Schreiben dieses Tests angeblich weniger anfällig für Codierungsfehler ist. Aberdiesem Ansatz fehlen einige Bereiche.

    • Wenn der Test zusätzliche Dinge in Bezug auf die Ausnahme wie die Ursache oder die Nachricht überprüfen muss (gute Ausnahmemeldungen sind wirklich wichtig, ein genauer Ausnahmetyp reicht möglicherweise nicht aus).
    • Auch wenn die Erwartung in der Methode herumsteht, kann je nachdem, wie der getestete Code geschrieben wurde, der falsche Teil des Testcodes die Ausnahme auslösen, was zu einem falsch positiven Test führt, und ich bin mir nicht sicher, obPMD,findbugsoderSonargibt Hinweise zu diesem Code.

      @Test(expected = WantedException.class)
      public void call2_should_throw_a_WantedException__not_call1() {
          // init tested
          tested.call1(); // may throw a WantedException
      
          // call to be actually tested
          tested.call2(); // the call that is supposed to raise an exception
      }
      
  3. Die ExpectedException-Regel ist auch ein Versuch, die vorherigen Vorbehalte zu beheben, aber es ist etwas umständlich, sie zu verwenden, da sie einen Erwartungsstil verwendet.EasyMockBenutzer kennen diesen Stil sehr gut. Für einige mag es praktisch sein, aber wenn SieBehavior Driven Development(BDD) oderArrange Act Assert( AAA) -Prinzipien, die die ExpectedException -Regel nicht in diesen Schreibstil passen. Abgesehen davon kann es unter dem gleichen Problem leiden wie beim @Test -Verfahren, je nachdem, wo Sie die Erwartung stellen.

    @Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    

    Selbst wenn die erwartete Ausnahme vor der Testanweisung steht, wird der Lesefluss unterbrochen, wenn die Tests BDD oder AAA folgen.

    Siehe auch diese Kommentar Ausgabe auf JUnit des Autors von ExpectedException. JUnit 4.13-beta-2 lehnt diesen Mechanismus sogar ab:

    Pull request # 1519 : ExpectedException veralten

    Die Methode Assert.assertThrows bietet eine bessere Möglichkeit zum Überprüfen von Ausnahmen. Darüber hinaus ist die Verwendung von ExpectedException bei der Verwendung mit anderen Regeln wie TestWatcher fehleranfällig, da in diesem Fall die Reihenfolge der Regeln wichtig ist.

Die oben genannten Optionen haben also jede Menge Vorbehalte und sind eindeutig nicht immun gegen Codierungsfehler.

  1. Es gibt ein Projekt, das mir aufgefallen ist, nachdem ich diese Antwort erstellt habe, die vielversprechend aussieht: catch-exception .

    Wie die Beschreibung des Projekts besagt, ließ es einen Codierer in einer fließenden Codezeile schreiben, die die Ausnahme abfängt, und bietet diese Ausnahme zur späteren Bestätigung an. Und Sie können jede Assertionsbibliothek wie Hamcrest oder AssertJ verwenden.

    Ein kurzes Beispiel von der Homepage:

    // given: an empty list
    List myList = new ArrayList();
    
    // when: we try to get the first element of the list
    when(myList).get(1);
    
    // then: we expect an IndexOutOfBoundsException
    then(caughtException())
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessage("Index: 1, Size: 0") 
            .hasNoCause();
    

    Wie Sie sehen, ist der Code wirklich unkompliziert. Sie erkennen die Ausnahme in einer bestimmten Zeile. Die API then ist ein Alias, der AssertJ-APIs verwendet (ähnlich wie bei der Verwendung von assertThat(ex).hasNoCause()...).Irgendwann stützte sich das Projekt auf FEST-Assert, den Vorfahren von AssertJ. EDIT:Es scheint, als würde das Projekt eine Java 8 Lambdas-Unterstützung brauen.

    Derzeit weist diese Bibliothek zwei Mängel auf:

    • Zum Zeitpunkt dieses Schreibens ist es bemerkenswert zu sagen, dass diese Bibliothek auf Mockito 1.x basiert, da sie eine Kopie des getesteten Objekts hinter den Kulissen erstellt. Da Mockito immer noch nicht aktualisiert wird , kann diese Bibliothek nicht mit finalen Klassen oder finalen Methodenarbeiten. Und selbst wenn es auf Mockito 2 in der aktuellen Version basieren würde, müsste dafür ein globaler Mock-Maker (inline-mock-maker) deklariert werden, was möglicherweise nicht Ihren Wünschen entspricht, da dieser Mock-Maker andere Nachteile hat als der normale Mock-Maker.

    • Es erfordert noch eine weitere Testabhängigkeit.

    Diese Probleme treten nicht auf, sobald die Bibliothek Lambdas unterstützt. Die Funktionalität wird jedoch vom AssertJ-Toolset dupliziert.

    Wenn Sie alle berücksichtigen, dass Sie das Tool zum Auffangen von Ausnahmen nicht verwenden möchten, empfehle ich den alten guten Weg des Blocks try-catch, zumindest bis zum JDK7. Und für JDK 8-Benutzer bevorzugen Sie möglicherweise die Verwendung von AssertJ, da es mehr bietet als nur Ausnahmen zu definieren.

  2. Mit dem JDK8 betreten Lambdas die Testszene und haben sich als interessante Möglichkeit erwiesen, außergewöhnliches Verhalten durchzusetzen. AssertJ wurde aktualisiert, um eine flüssige API für außergewöhnliches Verhalten bereitzustellen.

    Und ein Beispieltest mit AssertJ :

    @Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
    
  3. Mit einer fast vollständigen Neufassung von JUnit 5 wurden Behauptungen verbessert ein bisschen, sie könnten sich als eine out-of-the-box-Methode als interessant erweisen, um eine ordnungsgemäße Ausnahme zu behaupten. Aber die Assertion-API ist immer noch ein bisschen mangelhaft, es gibt nichts außerhalb von assertThrows .

    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }
    

    Wie Sie bemerkt haben, gibt assertEquals immer noch void zurück und erlaubt daher keine Verkettung von Zusicherungen wie AssertJ.

    Auch wenn Sie sich an einen Namenskonflikt mit Matcher oder Assert erinnern, sollten Sie darauf vorbereitet sein, den gleichen Konflikt mit Assertions zu führen.

Ich möchte daraus schließen, dass heute (03.03.2017) AssertJ's Benutzerfreundlichkeit, erkennbare API, schnelle Entwicklungstempo und alsde factoTestabhängigkeit ist die beste Lösung mit JDK8, unabhängig vom Testframework (JUnit oder nicht). Frühere JDKs sollten sich stattdessen auf try-catchblocks even verlassen wenn sie sich klobig fühlen.

Diese Antwort wurde kopiert von eine weitere Frage die nicht die gleiche Sichtbarkeit haben, ich bin der gleiche Autor.

102
Brice

Nach der Veröffentlichung von JUnit 5 ist die beste Option die Verwendung von Assertions.assertThrows() (siehe Junit 5 User Guide ).

Hier ist ein Beispiel, das überprüft, ob eine Ausnahme ausgelöst wurde, und Wahrheit verwendet, um Aussagen über die Ausnahmemeldung zu treffen:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

Die Vorteile gegenüber den Ansätzen in den anderen Antworten sind:

  1. In JUnit integriert
  2. Sie erhalten eine nützliche Ausnahmemeldung, wenn der Code im Lambda keine Ausnahme auslöst, und eine Stapelverfolgung, wenn eine andere Ausnahme ausgelöst wird
  3. Prägnant
  4. Ermöglicht Ihren Tests, Arrange-Act-Assert zu folgen
  5. Sie können genau angeben, von welchem ​​Code Sie erwarten, dass er die Ausnahme auslöst
  6. Sie müssen die erwartete Ausnahme nicht in der throws -Klausel auflisten
  7. Sie können das Assertion-Framework Ihrer Wahl verwenden, um Aussagen über die festgestellte Ausnahme zu treffen

Eine ähnliche Methode wird zu org.junit Assert in JUnit 4.13 hinzugefügt.

55
NamshubWriter

Wie wäre es damit: Fangen Sie eine sehr allgemeine Ausnahme ab, stellen Sie sicher, dass sie aus dem catch-Block entfernt wird, und stellen Sie dann sicher, dass die Klasse der Ausnahme Ihrer Erwartung entspricht. Diese Zusicherung schlägt fehl, wenn a) die Ausnahme vom falschen Typ ist (z. B. wenn Sie stattdessen einen Nullzeiger erhalten haben) und b) die Ausnahme nie ausgelöst wurde.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}
40
Johan

BDD Style Lösung: JUnit 4 + Catch Exception + AssertJ

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

Quellcode

Abhängigkeiten

eu.codearte.catch-exception:catch-exception:1.3.3
34
MariuszS

Verwenden einer AssertJ Assertion, die neben JUnit verwendet werden kann:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

Es ist besser als @Test(expected=IndexOutOfBoundsException.class), da es garantiert, dass die erwartete Zeile im Test die Ausnahme ausgelöst hat und Sie weitere Details zu der Ausnahme überprüfen können, z. B. message, einfacher:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Maven/Gradle Anweisungen hier.

33
weston

Um das gleiche Problem zu lösen, habe ich ein kleines Projekt eingerichtet: http://code.google.com/p/catch-exception/

Mit diesem kleinen Helfer würden Sie schreiben

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

Dies ist weniger ausführlich als die ExpectedException-Regel von JUnit 4.7. Im Vergleich zu der von skaffman bereitgestellten Lösung können Sie angeben, in welcher Codezeile Sie die Ausnahme erwarten. Ich hoffe das hilft.

32
rwitzel

Update: JUnit5 hat eine Verbesserung für das Testen von Ausnahmen: assertThrows.

das folgende Beispiel stammt aus: Junit 5-Benutzerhandbuch

 @Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
    {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

Ursprüngliche Antwort mit JUnit 4.

Es gibt verschiedene Möglichkeiten, um zu testen, ob eine Ausnahme ausgelöst wurde. Ich habe auch die folgenden Optionen in meinem Beitrag besprochen Wie man großartige Unit-Tests mit JUnit schreibt

Stellen Sie den expected Parameter @Test(expected = FileNotFoundException.class) ein.

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

trycatch verwenden

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }

}

Testen mit ExpectedException Regel.

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

@Test
public void testReadFile() throws FileNotFoundException {

    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

Weitere Informationen zum Testen von Ausnahmen finden Sie in JUnit4-Wiki für Ausnahmetests und bad.robot - Expecting Exceptions JUnit Rule .

30

Sie können dies auch tun:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}
21
John Mikic

Meiner Meinung nach ist der beste Weg, um in JUnit nach Ausnahmen zu suchen, das Muster try/catch/fail/assert:

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

Das assertTrue kann für einige Leute ein bisschen stark sein, daher ist assertThat(e.getMessage(), containsString("the message"); möglicherweise vorzuziehen.

13
Alex Collins

JUnit 5-Lösung

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

Weitere Informationen zu JUnit 5 finden Sie unter http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

12
Daniel Käfer

Ich habe hier viele Methoden ausprobiert, aber sie waren entweder kompliziert oder entsprachen nicht ganz meinen Anforderungen. Tatsächlich kann man ganz einfach eine Hilfsmethode schreiben:

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

Benutze es so:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

Null Abhängigkeiten: keine Notwendigkeit für Mockito, keine Notwendigkeit Powermock; und funktioniert gut mit Abschlussklassen.

11
Hugh Perkins

Die flexibelste und eleganteste Antwort für Junit 4 fand ich im Mkyong-Blog . Es hat die Flexibilität des try/catch unter Verwendung der @Rule -Anmerkung. Ich mag diesen Ansatz, weil Sie bestimmte Attribute einer benutzerdefinierten Ausnahme lesen können.

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

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

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}
10
Dherik

Java 8 Lösung

Wenn Sie eine Lösung wünschen, die:

  • Verwendet Java 8 Lambdas
  • Hängt nicht von irgendeiner JUnit-Magie ab
  • Ermöglicht die Überprüfung auf mehrere Ausnahmen innerhalb einer einzelnen Testmethode
  • Überprüft, ob eine Ausnahme von einem bestimmten Satz von Zeilen in Ihrer Testmethode ausgelöst wird, anstatt von einer unbekannten Zeile in der gesamten Testmethode
  • Ergibt das tatsächlich ausgelöste Ausnahmeobjekt, damit Sie es weiter untersuchen können

Hier ist eine Utility-Funktion, die ich geschrieben habe:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

( aus meinem Blog entnommen )

Verwenden Sie es wie folgt:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}
9
Mike Nakis

JUnit unterstützt dies mit einem "erwartet" -Attribut

8
Mark Bessey

In meinem Fall bekomme ich immer RuntimeException von db, aber die Meldungen unterscheiden sich. Und Ausnahme müssen jeweils behandelt werden. So habe ich es getestet:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}
7
Macchiatow

In JUnit 4 oder höher können Sie die Ausnahmen wie folgt testen

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


Dies bietet viele Funktionen, die zur Verbesserung unserer JUnit-Tests verwendet werden können.
Wenn Sie das folgende Beispiel sehen, teste ich 3 Dinge mit der Ausnahme.

  1. Der Typ der ausgelösten Ausnahme
  2. Die Ausnahme Nachricht
  3. Die Ursache der Ausnahme


public class MyTest {

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

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}
5
Jobin Joseph

Erstellen Sie einfach einen Matcher, der wie folgt ein- und ausgeschaltet werden kann:

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

Um es zu benutzen:

füge public ExpectedException exception = ExpectedException.none(); hinzu, dann:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
5
Tor P

Wir können einen Assertionsfehler nach der Methode verwenden, die eine Ausnahme zurückgeben muss:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
5
Shessuky

Zusätzlich zu dem, was NamShubWriter gesagt hat, stellen Sie sicher, dass:

  • Die ExpectedException-Instanz ist public ( Verwandte Frage )
  • Die ExpectedException wird nicht etwa in der @Before-Methode instanziiert . Dieses post erklärt deutlich alle Feinheiten der Ausführungsreihenfolge von JUnit.

Tun Sie nicht :

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

Schließlich zeigt this in einem Blog-Beitrag deutlich, wie eine bestimmte Ausnahme ausgelöst wird.

4
Bruce Wayne

Ich empfehle die Bibliothek assertj-core, um Ausnahmen im Juni-Test zu behandeln

In Java 8 wie folgt:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
3
Piotr R

Die Junit4-Lösung mit Java8 besteht darin, diese Funktion zu verwenden:

public Throwable assertThrows(Class<? extends Throwable> expectedException, Java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

Verwendung ist dann:

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

Beachten Sie, dass die einzige Einschränkung darin besteht, eine final -Objektreferenz im Lambda-Ausdruck zu verwenden. Diese Lösung ermöglicht es, Test-Assertions fortzusetzen, anstatt zu erwarten, dass sie auf Methodenebene mit der @Test(expected = IndexOutOfBoundsException.class) -Lösung aktiviert werden können.

2
Donatello

Nehmen wir zum Beispiel an, Sie möchten Junit für das unten genannte Codefragment schreiben

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

Der obige Code soll auf eine unbekannte Ausnahme testen, die auftreten kann, und der folgende Code soll eine Ausnahme mit einer benutzerdefinierten Nachricht bestätigen.

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

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
1
Shirsh Sinha
    @Test(expectedException=IndexOutOfBoundsException.class) 
    public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
         doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
         try {
             foo.doStuff(); 
            } catch (IndexOutOfBoundsException e) {
                       assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                      throw e;

               }

    }

Hier ist eine andere Möglichkeit, die Methode zu überprüfen, die die richtige Ausnahme ausgelöst hat oder nicht.

0
MangduYogii

Es gibt zwei Möglichkeiten, einen Testfall zu schreiben

  1. Kommentieren Sie den Test mit der Ausnahme, die von der Methode ausgelöst wird. So etwas wie dieses @Test(expected = IndexOutOfBoundsException.class)
  2. Sie können die Ausnahme in der Testklasse einfach mit dem try catch-Block abfangen und die Meldung bestätigen, die von der Methode in der Testklasse ausgegeben wird.

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

Ich hoffe dies beantwortet Ihre Frage Viel Spaß beim Lernen ...

0
Mohit ladia

Mit Java 8 können Sie eine Methode erstellen, die einen Code zur Überprüfung und die erwartete Ausnahme als Parameter verwendet:

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

und dann in deinem Test:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

Leistungen:

  • sich nicht auf irgendeine Bibliothek verlassen
  • lokalisierte Prüfung - genauer und ermöglicht bei Bedarf mehrere Aussagen wie diese in einem Test
  • einfach zu verwenden
0
fahrenx

Meine Lösung mit Java 8 Lambdas:

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

Sie müssen ein FunctionalInterface definieren, da Runnable nicht das erforderliche throws deklariert.

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

Die Methode kann wie folgt angewendet werden:

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
0
heio