it-swarm.com.de

Wie kann ich in Java eine ausgelöste Ausnahme mit JUnit überprüfen?

Wenn Sie Komponententests für eine Java-API schreiben, möchten Sie unter Umständen eine Ausnahmebedingung genauer überprüfen. Das heißt mehr als die @test Annotation von JUnit bietet.

Angenommen, eine Klasse sollte eine Ausnahme von einem anderen Interface abfangen, diese Ausnahme umbrechen und die umbrochene Ausnahme auslösen. Möglicherweise möchten Sie Folgendes überprüfen:

  1. Der genaue Methodenaufruf, der die umschlossene Ausnahme auslöst.
  2. Dass die Wrapper-Ausnahme die ursprüngliche Ausnahme als Ursache hat.
  3. Die Nachricht der Wrapper-Ausnahme.

Der wichtigste Punkt hierbei ist, dass Sie eine Ausnahme in einem Komponententest zusätzlich validieren möchten (keine Debatte darüber, ob Sie sollten Dinge wie die Ausnahmebedingungsnachricht überprüfen).

Was ist ein guter Ansatz dafür?

28
Iain

In JUnit 4 kann dies problemlos mit ExpectedException rule durchgeführt werden.

Hier ist ein Beispiel von Javadocs:

// These tests all pass.
public static class HasExpectedException {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void throwsNothing() {
        // no exception expected, none thrown: passes.
    }

    @Test
    public void throwsNullPointerException() {
        thrown.expect(NullPointerException.class);
        throw new NullPointerException();
    }

    @Test
    public void throwsNullPointerExceptionWithMessage() {
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened?");
        thrown.expectMessage(startsWith("What"));
        throw new NullPointerException("What happened?");
    }
}
21
Jonas

Wie in Ihre Antwort angegeben, ist dies ein guter Ansatz. Außerdem:

Sie können die Funktion expectException in eine neue Annotation namens ExpectedException einschließen.
Eine kommentierte Methode würde folgendermaßen aussehen:

@Test
@ExpectedException(class=WrapperException.class, message="Exception Message", causeException)
public void testAnExceptionWrappingFunction() {
  //whatever you test
}

Dieser Weg wäre besser lesbar, aber es ist genau der gleiche Ansatz.

Ein weiterer Grund ist: Ich mag Anmerkungen :)

24
guerda

Wenn Sie sich die vorgeschlagenen Antworten anschauen, können Sie wirklich den Schmerz spüren, wenn Sie keine Schließungen in Java haben. IMHO ist die am besten lesbare Lösung der gute alte Versuch.

@Test
public void test() {
    ...
    ...
    try {
        ...
        fail("No exception caught :(");
    }
    catch (RuntimeException ex) {
        assertEquals(Whatever.class, ex.getCause().getClass());
        assertEquals("Message", ex.getMessage());
    }
}
18
akuhn

Für JUNIT 3.x

public void test(){
   boolean thrown = false;
   try{
      mightThrowEx();
   } catch ( Surprise expected ){
      thrown = true;
      assertEquals( "message", expected.getMessage());
   }
  assertTrue(thrown );
}
11
mP.

Bis zu diesem Beitrag habe ich meine Ausnahmegenehmigung folgendermaßen durchgeführt:

try {
    myObject.doThings();
    fail("Should've thrown SomeException!");
} catch (SomeException e) {
    assertEquals("something", e.getSomething());
}

Ich verbrachte einige Momente damit, über das Problem nachzudenken und kam zu folgenden Themen (Java5, JUnit 3.x):

// Functor interface for exception assertion.
public interface AssertionContainer<T extends Throwable> {
    void invoke() throws T;
    void validate(T throwable);
    Class<T> getType();
}

// Actual assertion method.
public <T extends Throwable> void assertThrowsException(AssertionContainer<T> functor) {
    try {
        functor.invoke();
        fail("Should've thrown "+functor.getType()+"!");
    } catch (Throwable exc) {
        assertSame("Thrown exception was of the wrong type! Expected "+functor.getClass()+", actual "+exc.getType(),
                   exc.getClass(), functor.getType());
        functor.validate((T) exc);
    }
}

// Example implementation for servlet I used to actually test this. It was an inner class, actually.
AssertionContainer<ServletException> functor = new AssertionContainer<ServletException>() {
    public void invoke() throws ServletException {
        servlet.getRequiredParameter(request, "some_param");
    }

    public void validate(ServletException e) {
        assertEquals("Parameter \"some_param\" wasn't found!", e.getMessage());
    }

    public Class<ServletException> getType() {
        return ServletException.class;
    }
}

// And this is how it's used.
assertThrowsException(functor);

Wenn ich mir diese beiden ansehe, kann ich mich nicht entscheiden, welche mir mehr gefällt. Ich denke, dies ist eines der Probleme, bei denen das Erreichen eines Ziels (in meinem Fall die Assertion-Methode mit dem Parameter functor) sich auf lange Sicht nicht lohnt, da es einfach viel einfacher ist, diese 6+ Code auszuführen, um den Versuch zu bestätigen ..Fangblock.

Andererseits ist mein zehnminütiges Ergebnis der Problemlösung am Freitagabend vielleicht nicht der intelligenteste Weg, dies zu tun.

5
Esko

@akuhn:

Auch ohne Schließungen können wir eine lesbarere Lösung erhalten (mit catch-exception ):

import static com.googlecode.catchexception.CatchException.*;

public void test() {
    ...
    ...
    catchException(nastyBoy).doNastyStuff();
    assertTrue(caughtException() instanceof WhateverException);
    assertEquals("Message", caughtException().getMessage());
}
4
rwitzel

ich habe etwas sehr einfaches gemacht

testBla(){
    try {
        someFailingMethod()
        fail(); //method provided by junit
    } catch(Exception e) {
          //do nothing
    }
}
2

Die folgende Hilfsmethode (angepasst von this blogpost) macht den Trick:

/**
 * Run a test body expecting an exception of the
 * given class and with the given message.
 *
 * @param test              To be executed and is expected to throw the exception.
 * @param expectedException The type of the expected exception.
 * @param expectedMessage   If not null, should be the message of the expected exception.
 * @param expectedCause     If not null, should be the same as the cause of the received exception.
 */
public static void expectException(
        Runnable test,
        Class<? extends Throwable> expectedException,
        String expectedMessage,
        Throwable expectedCause) {
    try {
        test.run();
    }
    catch (Exception ex) {
        assertSame(expectedException, ex.getClass());
        if (expectedMessage != null) {
            assertEquals(expectedMessage, ex.getMessage());
        }

        if (expectedCause != null) {
            assertSame(expectedCause, ex.getCause());
        }

        return;
    }

    fail("Didn't find expected exception of type " + expectedException.getName());
}

Der Testcode kann dann wie folgt aufrufen:

TestHelper.expectException(
        new Runnable() {
            public void run() {
                classInstanceBeingTested.methodThatThrows();
            }
        },
        WrapperException.class,
        "Exception Message",
        causeException
);
2
Iain

Für JUnit 5 ist es viel einfacher:

    @Test
    void testAppleIsSweetAndRed() throws Exception {

        IllegalArgumentException ex = assertThrows(
                IllegalArgumentException.class,
                () -> testClass.appleIsSweetAndRed("orange", "red", "sweet"));

        assertEquals("this is the exception message", ex.getMessage());
        assertEquals(NullPointerException.class, ex.getCause().getClass());
    }

Durch die Rückgabe des Exception-Objekts selbst können Sie mit assertThrows() jeden Aspekt hinsichtlich Ihrer ausgelösten Exceptions testen.

0
Marcus K.

Ich habe einen Helfer gemacht, der den anderen geposteten ähnlich ist:

public class ExpectExceptionsExecutor {

    private ExpectExceptionsExecutor() {
    }

    public static  void execute(ExpectExceptionsTemplate e) {
        Class<? extends Throwable> aClass = e.getExpectedException();

        try {
            Method method = ExpectExceptionsTemplate.class.getMethod("doInttemplate");
            method.invoke(e);
        } catch (NoSuchMethodException e1) {


            throw new RuntimeException();
        } catch (InvocationTargetException e1) {


            Throwable throwable = e1.getTargetException();
            if (!aClass.isAssignableFrom(throwable.getClass())) {
                //  assert false
                fail("Exception isn't the one expected");
            } else {
                assertTrue("Exception captured ", true);
                return;
            }
            ;


        } catch (IllegalAccessException e1) {
            throw new RuntimeException();
        }

        fail("No exception has been thrown");
    }


}

Und die Vorlage, die der Client implementieren soll 

public interface ExpectExceptionsTemplate<T extends Throwable> {


    /**
     * Specify the type of exception that doInttemplate is expected to throw
     * @return
     */
    Class<T> getExpectedException();


    /**
     * Execute risky code inside this method
     * TODO specify expected exception using an annotation
     */
    public void doInttemplate();

}

Und der Client-Code würde ungefähr so ​​aussehen:

@Test
    public void myTest() throws Exception {
        ExpectExceptionsExecutor.execute(new ExpectExceptionsTemplate() {
            @Override
            public Class getExpectedException() {
                return IllegalArgumentException.class;
            }

            @Override
            public void doInttemplate() {
                riskyMethod.doSomething(null);
            }
        });
     }

Es sieht sehr wortreich aus, aber wenn Sie ein IDE mit guter Autovervollständigung verwenden, müssen Sie nur den Typ der Ausnahme und den tatsächlich getesteten Code schreiben. (den Rest erledigt das IDE: D)

0
Jaime Hablutzel