Hoe beweert u dat een bepaalde uitzondering wordt gegenereerd in JUnit 4-tests?

Hoe kan ik JUnit4 idiomatisch gebruiken om te testen of een code een uitzondering genereert?

Terwijl ik zeker zoiets kan doen:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;
  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }
  assertTrue(thrown);
}

Ik herinner me dat er een annotatie of een Assert.xyz of ietsis dat veel minder onhandig is en veel meer in de geest van JUnit voor dit soort situaties.


Antwoord 1, autoriteit 100%

Het hangt af van de JUnit-versie en welke assertbibliotheken je gebruikt.

Het oorspronkelijke antwoord voor JUnit <= 4.12was:

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

Hoewel het antwoord https://stackoverflow.com/a/31826781/2986984meer opties heeft voor JUnit <= 4.12.

Referentie:


Antwoord 2, autoriteit 55%

Bewerken:Nu JUnit 5 en JUnit 4.13 zijn uitgebracht, zou de beste optie zijn om Assertions.assertThrows()(voor JUnit 5) en Assert.assertThrows()(voor JUnit 4.13+). Zie mijn andere antwoordvoor details.

Als je niet bent gemigreerd naar JUnit 5, maar wel JUnit 4.7 kunt gebruiken, kun je de ExpectedExceptionRegel:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();
    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

Dit is veel beter dan @Test(expected=IndexOutOfBoundsException.class)omdat de test zal mislukken als IndexOutOfBoundsExceptionwordt gegenereerd vóór foo.doStuff()

Zie dit artikelvoor details.


Antwoord 3, autoriteit 20%

Wees voorzichtig met het gebruik van verwachte uitzonderingen, omdat het alleen beweert dat de methodedie uitzondering veroorzaakte, niet een bepaalde regel codein de test.

Ik gebruik dit meestal voor het testen van parametervalidatie, omdat dergelijke methoden meestal erg eenvoudig zijn, maar complexere tests kunnen beter worden uitgevoerd met:

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

Pas het oordeel toe.


4, Autoriteit 9%

Zoals eerder beantwoord, zijn er veel manieren om met uitzonderingen in Junit te handelen. Maar met Java 8 is er nog een: met behulp van Lambda Expressions. Met Lambda Expressions kunnen we een syntaxis behalen als volgt:

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

Asserthrown accepteert een functionele interface, waarvan de instanties kunnen worden gemaakt met lambda-uitdrukkingen, methode-referenties of constructorreferenties. Asserterthrown Accepting dat interface zal verwachten en klaar zijn om een ​​uitzondering aan te pakken.

Dit is relatief eenvoudige maar krachtige techniek.

Bekijk deze blog post die deze techniek beschrijft: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

De broncode is hier te vinden: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

Disclosure: ik ben de auteur van de blog en het project.


Antwoord 5, autoriteit 8%

in junit zijn er vier manieren om uitzonderingen te testen.

junit5.x

  • voor junit5.x kunt u assertThrowsals volgt gebruiken

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

junt4.x

  • gebruik voor junit4.x het optionele kenmerk ‘expected’ van Testannonation

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • gebruik voor junit4.x de regel ExpectedException

    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();
        }
    }
    
  • je kunt ook de klassieke try/catch-manier gebruiken die veel wordt gebruikt onder junit 3-framework

    @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.
        }
    }
    
  • SO

    • Als u van JUNIST 5 houdt, dan zou u de 1e
    • willen

    • De 2e manier wordt gebruikt wanneer u alleen het type uitzondering wilt testen
    • De eerste en laatste twee worden gebruikt wanneer u een bericht wilt uitzondering verder
    • Als u Junit 3 gebruikt, dan heeft de 4e de voorkeur
  • Voor meer info kunt u dit document lezen en Junit5 Gebruikershandleiding voor meer informatie.


6, Autoriteit 3%

Nu Junit 5 en Junit 4.13 zijn vrijgegeven, zou de beste optie zijn om Assertions.assertThrows()(voor JUNIST 5) en Assert.assertThrows()(voor JUBIT 4.13). Zien
The junit 5 gebruikershandleiding .

Hier is een voorbeeld dat een uitzondering verifieert wordt gegooid en gebruikt waarheid om beweringen aan te brengen Het uitzonderingsbericht:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();
    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);
    assertThat(e).hasMessageThat().contains("woops!");
  }
}

De voordelen boven de benaderingen in de andere antwoorden zijn:

  1. ingebouwd in JUNIST
  2. U krijgt een nuttig uitzonderingsbericht als de code in de Lambda geen uitzondering gooit, en een stacktrace als het een andere uitzondering
  3. gooit

  4. Beknopt
  5. Hiermee kunt u uw tests regelen-ACT-ASSERT
  6. volgen

  7. U kunt precies aangeven welke code u verwacht de uitzondering te gooien
  8. U hoeft de verwachte uitzondering in de throwsCLAUSE
  9. weer te geven

  10. U kunt het beweringskader van uw keuze gebruiken om beweringen te maken over de betrapt uitzondering

7, Autoriteit 2%

Hoe zit het met dit: vang een zeer algemene uitzondering, zorg ervoor dat het uit het vangstblok maakt en beweert dat de klasse van de uitzondering is wat u verwacht. Deze assert zal falen als een) de uitzondering is van het verkeerde type (bijv. Als u in plaats daarvan een nul-aanwijzer hebt) en B) de uitzondering niet geworpen.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;
  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }
  assertTrue(e instanceof IndexOutOfBoundsException);
}

8, Autoriteit 2%

Gebruik een assertj bewering, die kan worden gebruikt naast JUNIST:

import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();
  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

Het is beter dan @Test(expected=IndexOutOfBoundsException.class)omdat het garandeert dat de verwachte lijn in de test de uitzondering gooide en kunt u meer details controleren over de uitzondering, zoals bericht, eenvoudiger:

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

MAVEN / ADRUTLE INSTRUCTIES HIER.


9, Autoriteit 2%

BDD-stijloplossing: JUnit 4+ Vanguitzondering+ AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
    when(() -> foo.doStuff());
    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}

Afhankelijkheden

eu.codearte.catch-exception:catch-exception:2.0

Antwoord 10, autoriteit 2%

Update:JUnit5 heeft een verbetering voor het testen van uitzonderingen: assertThrows.

Het volgende voorbeeld komt uit: Junit 5 Gebruikershandleiding

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

Oorspronkelijk antwoord met JUnit 4.

Er zijn verschillende manieren om te testen of er een uitzondering wordt gegenereerd. Ik heb ook de onderstaande opties besproken in mijn post Hoe geweldige unit-tests te schrijven met JUnit

Stel de expectedparameter @Test(expected = FileNotFoundException.class)in.

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

Gebruik trycatch

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 met de regel ExpectedException.

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

Je kunt meer lezen over het testen van uitzonderingen in JUnit4-wiki voor het testen van uitzonderingenen bad.robot – Verwacht Uitzonderingen JUnit-regel.


Antwoord 11

Om hetzelfde probleem op te lossen heb ik een klein project opgezet:
http://code.google.com/p/catch-exception/

Met behulp van deze kleine helper zou je schrijven

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

Dit is minder uitgebreid dan de ExpectedException-regel van JUnit 4.7.
In vergelijking met de oplossing van skaffman, kunt u aangeven in welke regel code u de uitzondering verwacht. Ik hoop dat dit helpt.


Antwoord 12

U kunt dit ook doen:

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

Antwoord 13

IMHO, de beste manier om te controleren op uitzonderingen in JUnit is het patroon try/catch/fail/asset:

// 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()); 
}

De assertTrueis misschien wat sterk voor sommige mensen, dus assertThat(e.getMessage(), containsString("the message");kan de voorkeur hebben.


Antwoord 14

Het meest flexibele en elegante antwoord voor Junit 4 vond ik in de Mkyong-blog. Het heeft de flexibiliteit van de try/catchmet behulp van de @Ruleannotatie. Ik hou van deze aanpak omdat je specifieke kenmerken van een aangepaste uitzondering kunt lezen.

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("");
    }
}

Antwoord 15

JUnit 5-oplossing

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  IndexOutOfBoundsException exception = expectThrows(IndexOutOfBoundsException.class, foo::doStuff);
  assertEquals("some message", exception.getMessage());
}

Meer informatie over JUnit 5 op http:// junit.org/junit5/docs/current/user-guide/#writing-tests-assertions


Antwoord 16

Ik heb veel van de methoden hier geprobeerd, maar ze waren ofwel ingewikkeld of voldeden niet helemaal aan mijn eisen. In feite kan men heel eenvoudig een helpermethode schrijven:

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

Gebruik het als volgt:

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

Nul afhankelijkheden: geen mockito nodig, geen powermock nodig; en werkt prima met eindklassen.


Antwoord 17

JUnit heeft hiervoor ingebouwde ondersteuning, met een “expected” attribuut .


Antwoord 18

Java 8-oplossing

Als u een oplossing wilt die:

  • Gebruikt Java 8 lambda’s
  • Is nietafhankelijk van enige JUnit-magie
  • Hiermee kunt u controleren op meerdere uitzonderingen binnen een enkele testmethode
  • Controleert of er een uitzondering wordt gegenereerd door een specifieke set regels binnen uw testmethode in plaats van een onbekende regel in de gehele testmethode
  • Geeft het daadwerkelijke uitzonderingsobject dat is gegenereerd, zodat u het verder kunt onderzoeken

Hier is een hulpprogramma dat ik heb geschreven:

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 testing for "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.
}

(overgenomen van mijn blog)

Gebruik het als volgt:

@Test
public void testMyFunction()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            myFunction();
        } );
    assert e.getMessage().equals( "I haz fail!" );
}
public void myFunction()
{
    throw new RuntimeException( "I haz fail!" );
}

Antwoord 19

In mijn geval krijg ik altijd RuntimeException van db, maar de berichten verschillen. En uitzonderingen moeten respectievelijk worden afgehandeld. Hier is hoe ik het heb getest:

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

Antwoord 20

Maak gewoon een Matcher die kan worden in- en uitgeschakeld, zoals deze:

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");
    }
}

Om het te gebruiken:

voeg public ExpectedException exception = ExpectedException.none();,
dan:

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

21

In Junit 4 of later kunt u de uitzonderingen als volgt testen

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

Dit biedt veel functies die kunnen worden gebruikt om onze Junit-tests te verbeteren.
Als u het onderstaande voorbeeld ziet, test ik 3 dingen op de uitzondering.

  1. Het type uitzondering gegooid
  2. het uitzonderingsbericht
  3. de oorzaak van de uitzondering

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");
    }
}

22

We kunnen een bewering gebruiken na de methode die een uitzondering moet retourneren:

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());
}

23

Bovendien op wat Namshubwriter heeft gezegd, zorg ervoor dat:

  • De verwachtexceptie is openbaar (gerelateerde vraag )
  • De verwachtexception is niet Instantiated in Say, de @Before-methode. Deze post verklaart duidelijk alle fijnequaten van de uitvoering van JUNIST.

Doe niet doe dit:

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

Ten slotte, dit Blog-bericht illustreert duidelijk hoe te doen dat een bepaalde uitzondering wordt gegooid.


24

Junit4-oplossing met Java8 is om deze functie te gebruiken:

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

Gebruik is dan:

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

Merk op dat de enige beperking het gebruik van een finalobjectreferentie in lambda-expressie is.
Deze oplossing maakt het mogelijk om door te gaan met het testen van beweringen in plaats van thowable op methodeniveau te verwachten met behulp van de @Test(expected = IndexOutOfBoundsException.class)-oplossing.


Antwoord 25

Ik raad bibliotheek assertj-coreaan om uitzonderingen in junit-test af te handelen

In Java 8, als volgt:

//given
//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));
//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);

Antwoord 26

Neem bijvoorbeeld dat je Junit wilt schrijven voor het onderstaande codefragment

public int divideByZeroDemo(int a,int b){
    return a/b;
}
public void exceptionWithMessage(String [] arr){
    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

De bovenstaande code is om te testen op een onbekende uitzondering die kan optreden en de onderstaande is om een uitzondering te bevestigen met een aangepast bericht.

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

Antwoord 27

Met Java 8 kun je een methode maken die een code gebruikt om te controleren en de verwachte uitzondering als parameters:

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

en dan in je test:

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

Voordelen:

  • niet afhankelijk zijn van een bibliotheek
  • gelokaliseerde controle – nauwkeuriger en maakt het mogelijk om meerdere beweringen zoals deze binnen één test te hebben indien nodig
  • gemakkelijk te gebruiken

Antwoord 28

   @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 is een andere manier om de methode-gegooide correcte uitzondering of niet te controleren.

Other episodes