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.
- Voor JUnit5 en 4.13 zie antwoord https://stackoverflow.com/a/2935935/2986984
- Als je assertJ of google-truth gebruikt, zie je antwoord https://stackoverflow.com/a/41019785/2986984
Het oorspronkelijke antwoord voor JUnit <= 4.12
was:
@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 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 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
assertThrows
als 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
- 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
willen
-
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:
- ingebouwd in JUNIST
- U krijgt een nuttig uitzonderingsbericht als de code in de Lambda geen uitzondering gooit, en een stacktrace als het een andere uitzondering
- Beknopt
- Hiermee kunt u uw tests regelen-ACT-ASSERT
- U kunt precies aangeven welke code u verwacht de uitzondering te gooien
- U hoeft de verwachte uitzondering in de
throws
CLAUSE - U kunt het beweringskader van uw keuze gebruiken om beweringen te maken over de betrapt uitzondering
gooit
volgen
weer te geven
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 expected
parameter @Test(expected = FileNotFoundException.class)
in.
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Gebruik try
catch
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 assertTrue
is 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/catch
met behulp van de @Rule
annotatie. 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.
}
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.
- Het type uitzondering gegooid
- het uitzonderingsbericht
- 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 final
objectreferentie 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-core
aan 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.