Vraag Hoe beweer je dat een zekere uitzondering in JUnit 4-tests wordt gegooid?


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

Hoewel ik zeker zoiets als dit 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 is of een Assert.xyz of iets dat is veel minder kludgy en veel meer in de geest van JUnit voor dit soort situaties.


1621
2017-10-01 06:56


oorsprong


antwoorden:


JUnit 4 heeft hiervoor ondersteuning:

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

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


1942
2017-10-01 07:12



Bewerk Nu JUnit5 is uitgebracht, is de beste optie om te gebruiken Assertions.assertThrows() (zien mijn andere antwoord).

Als u nog niet naar JUnit 5 bent gemigreerd, maar JUnit 4.7 kunt gebruiken, kunt u de ExpectedException Regel:

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 IndexOutOfBoundsException wordt eerder gegooid foo.doStuff()

Zien Dit artikel voor details


1165
2018-05-29 17:16



Wees voorzichtig met de verwachte uitzondering, omdat het alleen beweert dat het methode gooide die uitzondering, niet a bepaalde regel code in de test.

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

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

Beoordeling toepassen.


408
2017-10-01 09:31



Zoals eerder beantwoord, zijn er veel manieren om met uitzonderingen om te gaan in JUnit. Maar met Java 8 is er nog een andere: Lambda Expressions gebruiken. Met Lambda Expressions kunnen we een syntaxis als deze realiseren:

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

assertThrown accepteert een functionele interface, waarvan de instanties kunnen worden gemaakt met lambda-expressies, methodeverwijzingen of constructorreferenties. assertThrown accepteren dat de interface een uitzondering verwacht en klaar zal zijn om ermee om te gaan.

Dit is een relatief eenvoudige maar krachtige techniek.

Bekijk deze blogpost over deze techniek: 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

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


187
2017-07-07 22:35



in junit zijn er vier manieren om een ​​uitzondering te testen.

  • voor junit4.x, gebruik het optionele 'verwachte' attribuut van Test annonatie

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • voor junit4.x gebruikt u 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 het kader van junit 3

    @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.
        }
    }
    
  • ten slotte kunt u voor junit5.x ook assertThrows gebruiken als volgt

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

    • de 1e manier wordt gebruikt wanneer u alleen het type uitzondering wilt testen
    • de andere drie manieren worden gebruikt wanneer u het testuitzonderingsbericht verder wilt
    • als u junit 3 gebruikt, heeft de derde voorkeur
    • als je van junit 5 houdt, zou je de 4 leuk moeten vinden
  • voor meer informatie, kunt u lezen dit document en gebruikershandleiding junit5 voor details.


84
2017-08-05 08:05



tl; Dr.

  • pre-JDK8: Ik zal het oude goed aanbevelen try-catch blok.

  • post-JDK8: gebruik AssertJ of aangepaste lambda's om te beweren buitengewoon gedrag.

Ongeacht Junit 4 of JUnit 5.

het lange verhaal

Het is mogelijk om jezelf te schrijven doe het zelf  try-catch blokkeer of gebruik de JUnit-tools (@Test(expected = ...) of de @Rule ExpectedException JUnit-regelfunctie).

Maar deze manier zijn niet zo elegant en mengen niet goed leesbaarheid verstandig met andere hulpmiddelen.

  1. De try-catch blok moet je het blok rond het geteste gedrag schrijven, en schrijf de bewering in het catch-blok, dat is misschien in orde maar veel vinden dat deze stijl de leesstroom van een test onderbreekt. Ook moet je een schrijven Assert.fail aan het einde van de try blokkeren anders kan de test een kant van de beweringen missen; PMD, FindBugs of Sonar zullen dergelijke problemen opmerken.

  2. De @Test(expected = ...) feature is interessant omdat je minder code kunt schrijven en dan is het schrijven van deze test vermoedelijk minder gevoelig voor codeerfouten. Maar Deze benadering mist een aantal gebieden.

    • Als de test aanvullende zaken over de uitzondering, zoals de oorzaak of het bericht, moet controleren (goede uitzonderingsmeldingen zijn erg belangrijk, een precieze uitzonderingstype is misschien niet genoeg).
    • Ook als de verwachting rond in de methode wordt geplaatst, afhankelijk van hoe de geteste code is geschreven, kan het verkeerde deel van de testcode de uitzondering weggooien, wat leidt tot vals-positieve tests en ik weet het niet zeker PMD, FindBugs of Sonar geeft hints voor dergelijke 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. De ExpectedException regel is ook een poging om de vorige voorbehouden te herstellen, maar het voelt een beetje lastig om te gebruiken omdat het een verwachtingsstijl gebruikt, easymock gebruikers kennen deze stijl heel goed. Het kan voor sommigen handig zijn, maar als je het volgt Gedragsgerichte ontwikkeling (BDD) of Regel Act Assert (AAA) principes de ExpectedException regel past niet in die schrijfstijl. Afgezien daarvan kan het dezelfde kwestie hebben als de @Test manier, afhankelijk van waar u de verwachting plaatst.

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

    Zelfs de verwachte uitzondering wordt geplaatst vóór de testinstructie, het breekt uw leesstroom als de tests BDD of AAA volgen.

    Zie ook dit commentaar kwestie op JUnit van de auteur van ExpectedException.

Dus deze bovenstaande opties hebben allemaal hun lading aan kanttekeningen en zijn duidelijk niet immuun voor coderfouten.

  1. Er is een project dat ik me bewust werd na het maken van dit antwoord dat er veelbelovend uitziet, het is catch-uitzondering.

    Zoals de beschrijving van het project zegt, laat het een codeur schrijven in een vloeiende coderegel om de uitzondering te vangen en deze uitzondering te bieden voor latere bewering. En je kunt elke assertiebibliotheek gebruiken zoals Hamcrest of AssertJ.

    Een snel voorbeeld van de startpagina:

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

    Zoals je kunt zien is de code heel eenvoudig, je krijgt de uitzondering op een specifieke regel, de then API is een alias die AssertJ-API's zal gebruiken (vergelijkbaar met gebruiken assertThat(ex).hasNoCause()...). Op een gegeven moment vertrouwde het project op FEST-Assert de voorouder van AssertJ. BEWERK: Het lijkt erop dat het project een Java 8 Lambdas-ondersteuning aan het brouwen is.

    Momenteel heeft deze bibliotheek twee tekortkomingen:

    • Op het moment van dit schrijven is het opmerkelijk om te zeggen dat deze bibliotheek gebaseerd is op Mockito 1.x omdat het een schijn van het geteste object achter de schermen creëert. Omdat Mockito nog steeds niet is bijgewerkt deze bibliotheek kan niet werken met laatste klassen of definitieve methoden. En zelfs als het gebaseerd zou zijn op mockito 2 in de huidige versie, zou dit een globale schijnmaker moeten verklaren (inline-mock-maker), iets dat niet kan wat je wilt, omdat deze mockmaker andere nadelen heeft dan de gewone mockmaker.

    • Het vereist nog een testafhankelijkheid.

    Deze problemen zijn niet van toepassing zodra de bibliotheek lambda ondersteunt, maar de functionaliteit zal worden gedupliceerd door de AssertJ-toolset.

    Rekening houdend met alles als je de catch-exception tool niet wilt gebruiken, zal ik de oude goede weg van de try-catch blokkeren, in ieder geval tot aan de JDK7. En voor JDK 8-gebruikers zou je misschien AssertJ willen gebruiken omdat het aanbod meer kan dan alleen maar uitzonderingen beweren.

  2. Met de JDK8 betreden lambda's de testscene en ze hebben bewezen een interessante manier te zijn om uitzonderlijk gedrag te bewijzen. AssertJ is geüpdatet om een ​​mooie vloeiende API te bieden voor uitzonderlijk gedrag.

    En een voorbeeldtest met 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. Met een bijna volledige herschrijving van JUnit 5, zijn de beweringen geweest verbeterd een beetje, ze kunnen interessant zijn als een out-of-the-box manier om een ​​juiste uitzondering te claimen. Maar eigenlijk is de assertieve API nog steeds een beetje pover, er is niets buiten assertThrows.

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

    Zoals je gemerkt assertEquals komt nog steeds terug void, en als zodanig geen kettingreacties zoals AssertJ toestaat.

    Ook als je naam clash kent met Matcher of Assert, wees voorbereid op dezelfde botsing met Assertions.

Ik zou graag willen concluderen dat vandaag (2017-03-03) AssertJgebruiksgemak, vindbare API, snel tempo van ontwikkeling en als een de facto testafhankelijkheid is de beste oplossing met JDK8, ongeacht het testraamwerk (JUnit of niet), eerdere JDK's moeten in plaats daarvan vertrouwen op try-catch blokkeert zelfs als ze zich onhandig voelen.

Dit antwoord is overgenomen van een andere vraag die niet dezelfde zichtbaarheid hebben, ik ben dezelfde auteur.


58
2017-12-07 14:19



BDD Stijloplossing: JUnit 4 + Catch Exception + AssertJ

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

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

}

Broncode

afhankelijkheden

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

31
2017-11-15 19:17