Cum pot folosi JUnit4 idiomatically pentru a testa un cod aruncă o excepție?
În timp ce sigur pot face ceva de genul asta:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Îmi amintesc că există o adnotare sau o Afirma.xyz sau ceva care este mult mai puțin nepotrivite și mult mai mult în spiritul de JUnit pentru aceste tipuri de situații.
JUnit 4
are suport pentru acest lucru:
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
Referință :
Edit Acum, că JUnit5 a lansat, cea mai bună opțiune ar fi de a folosi Afirmațiile.assertThrows()` (vezi - mi alt răspuns).
Dacă te-ai't migrat la JUnit 5, dar se poate folosi JUnit 4.7, puteți utiliza ExpectedException
Regula:
public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}
Acest lucru este mult mai bine decât @Test(expected=IndexOutOfBoundsException.class)
deoarece testul va eșua în cazul IndexOutOfBoundsException
este aruncat înainte de foo.doStuff()
A se vedea acest articol pentru detalii
Fii atent, folosind așteptat excepție, pentru că doar afirmă că ** metode de aruncat excepție, nu o o linie de cod** în test.
Am tendința de a folosi acest lucru pentru testarea parametrilor de validare, pentru că astfel de metode sunt, de obicei, foarte simplu, dar mult mai complex de teste ar putea fi mai bine servit cu:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Aplica judecata.
Ca răspuns înainte, există multe moduri de a face cu excepții în JUnit. Dar cu Java 8 nu este un alt unul: folosind Expresii Lambda. Cu Expresii Lambda putem realiza o sintaxa de genul asta:
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
assertThrown acceptă o interfață funcțională, a cărei instanțe pot fi create cu expresii lambda, metoda de referințe, sau constructor de referințe. assertThrown acceptarea faptului că interfața va aștepta și va fi gata să se ocupe de o excepție.
Acest lucru este relativ simplu, dar puternic, tehnica.
Avea o privire de la acest post pe blog care descrie aceasta tehnica: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
Codul sursă poate fi găsit aici: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
Dezvaluirea: eu sunt autor de blog și de proiect.
în junit, există patru modalități de a testa excepție.
assertThrows
după cum urmează@Test public void testFooThrowsIndexOutOfBoundsException() { Throwable excepție = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff()); assertEquals("de așteptat mesaje", excepție.getMessage()); }
@Test(temperatura = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }
public class XxxTest { @Regula publice ExpectedException aruncat = ExpectedException.nici unul();
@Test public void testFooThrowsIndexOutOfBoundsException() { aruncat.aștepta(IndexOutOfBoundsException.class) //puteți testa excepție mesaj de genul aruncat.expectMessage("de așteptat mesaje"); foo.doStuff(); } }
@Test public void testFooThrowsIndexOutOfBoundsException() { try { foo.doStuff(); nu("de așteptat excepție a fost nu a avut loc."); } catch(IndexOutOfBoundsException e) { //dacă executarea ajunge aici, //se indică această excepție s-a întâmplat. //deci nu trebuie să-l ocupe. } }
deci
dacă vă place junit 5, atunci ar trebui ca 1 o
al 2-lea mod este utilizat atunci când doriți doar test de tipul de excepție
primele și ultimele două sunt utilizate atunci când doriți test de excepție mesajul mai departe
dacă utilizați junit 3, apoi 4 unul este de preferat
pentru mai multe informații, puteți citi acest document și junit5 ghid de utilizare pentru detalii.
tl;dr
fail()
afirmația înainte de a "prinde" block_) @Test(temperatura = ...) " sau " @Regula ExpectedException
JUnit regula caracteristică).
Dar aceste fel nu sunt atât de elegant și don't se amestecă bine readability wise cu alte instrumente. Mai mult decât atât JUnit scule nu au unele capcane. încerc
blocare în caz contrar, testul poate lipsi de o parte din afirmații ; PMD, findbugs sau Sonar vor depista astfel de probleme. @Test(temperatura = ...)
caracteristică este interesant ca poti scrie mai puțin cod și apoi scris acest test se presupune că este mai puțin predispus la erori de programare. Dar mii de abordare este lipsită de o anumite zone. // apel pentru a fi testat
testat.call2(); // apel, care ar trebui să ridice o excepție
}
3. Anii ExpectedException
regula este, de asemenea, o încercare de a repara anterior limitări, dar se simte un pic ciudat de a folosi ca foloseste o așteptare stil, EasyMock utilizatorii știe foarte bine acest stil. Ar fi convenabil pentru unii, dar dacă urmați Behaviour Condus Development (BDD) sau Arrange Act Assert (AAA) principii ExpectedException regula câștigat't se potrivesc în aceste stilul de scris. În afară de faptul că poate suferă de aceeași problemă ca la ca
@Test de drum, în funcție de cazul în care vă puneți speranța.
@Regula ExpectedException aruncat = ExpectedException.nici unul()
@Test
public void call2_trebuie_arunce_o_WantedException__nu_call1() {
// așteptările
aruncat.aștepta(WantedException.class);
aruncat.expectMessage("boom");
// init testat
testat.call1(); // poate arunca o WantedException
// apel pentru a fi testat testat.call2(); // apel, care ar trebui să ridice o excepție } Chiar de așteptat excepție este plasat înainte de declarația de încercare, se rupe dvs. de lectură de debit în cazul în care testele urmați BDD sau AAA. Vedea, de asemenea, acest comentariu problema pe JUnit de autor de ExpectedException`. JUnit 4.13-beta-2 chiar deprecates acest mecanism:
cerere de tragere #1519: Condamna ExpectedException
metoda se Afirme.assertThrows oferă un mod frumos pentru verificarea excepții. În plus, utilizarea de ExpectedException este predispusă la erori atunci când este utilizat cu alte reguli, cum ar fi TestWatcher pentru că ordinea de reguli este important în acest caz. Deci, aceste opțiunile de mai sus au toate lor de încărcare de limitări, și în mod clar nu este imun la coder erori. 4. Nu's a proiect am devenit conștient după crearea acestui răspuns care arata promitator, l's prinde-excepție. Ca descriere a proiectului spune, se lasa un programator scrie într-o fluent linie de cod prinderea excepție și de a oferi această excepție pentru mai târziu afirmație. Și puteți folosi orice afirmarea bibliotecii ca Hamcrest sau AssertJ.
O rapidă exemplu luat de pe pagina de start :
// date: o listă goală Lista myList = new ArrayList();
// când: - am să încercați să obțineți primul element al listei când(myList).ia(1);
// apoi: ne așteptăm la o IndexOutOfBoundsException apoi(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Size: 0") .hasNoCause();
După cum puteți vedea codul este foarte simplu, te prinde excepție pe o anumită linie, "apoi" API este un alias care va folosi AssertJ APIs (similar cu utilizarea assertThat(ex).hasNoCause()...
). At un moment dat proiectul s-a bazat pe FEST-și Afirme strămoșul AssertJ. EDIT: Se pare că proiectul este berii o Java 8 Lambda sprijin.
În prezent, această bibliotecă are două neajunsuri :
inline-mock-maker
), ceva care nu poate ce vrei tu, ca acest mockmaker are diferite dezavantaje periodice mockmaker. Aceste probleme câștigat't se aplică o dată la bibliotecă va sprijini lambda, cu toate acestea, funcționalitatea va fi copiată de AssertJ set de instrumente. Luând în considerare toate aceste aspecte, dacă nu't doriți să utilizați prinde-excepție instrument, eu va recomand vechi modalitate buna de a "încerca" - "prinde" de bloc, cel puțin până la JDK7. Și pentru JDK 8 utilizatori s-ar putea prefera să folosească AssertJ, deoarece oferă mai mult decât doar afirmarea excepții. 5. Cu JDK8, lambda intra pe scena de testare, și ei s-au dovedit a fi un mod interesant de a-și afirma excepționale comportament. AssertJ a fost actualizat pentru a oferi un API fluent pentru a afirma excepționale comportament. Și o probă cu 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() { ... // când Throwable aruncat = catchThrowable(() -> someBadIOOperation());
// apoi
assertThat(aruncat).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
}
6. Cu o rescriere completă a JUnit 5, afirmațiile au fost îmbunătățirea un pic, se poate dovedi interesant ca scos din cutie mod de a afirma în mod corespunzător excepție. Dar într-adevăr afirmația API este încă un pic mai slab, acolo's nimic în afara assertThrows
.
@Test
@DisplayName("aruncă EmptyStackException când am tras cu ochiul")
void throwsExceptionWhenPeeked() {
Throwable t = assertThrows(EmptyStackException.class, () -> stiva.peek());
Afirmațiile.assertEquals("...", t.getMessage());
}
După cum ați observat assertEquals
este încă revenind void
, și, ca atare, nu't permite înlănțuirea afirmații ca AssertJ.
De asemenea, dacă vă amintiți numele conflict cu Meciuri " sau " Afirma, să fie pregătit să îndeplinească aceleași conflict cu Afirmațiile
.
Am'd place să concluzioneze că astăzi (2017-03-03) AssertJ's usurinta de utilizare, poate fi descoperit API, ritmul rapid de dezvoltare și ca o de facto test de dependență este cea mai bună soluție cu JDK8 indiferent de cadru de testare (JUnit sau nu), înainte de JDKs ar trebui în schimb să se bazeze pe "încerca" - "prinde" blocuri, chiar dacă se simt greoaie.
This răspunsul a fost copiat de la o altă întrebare care nu't au aceeași vizibilitate, eu sunt același autor.
Acum, că JUnit 5 a lansat, cea mai bună opțiune este de a folosi Afirmațiile.assertThrows()` (a se vedea de Junit 5 Ghidul Utilizatorului).
Aici este un exemplu care verifică dacă o excepție este aruncată, și folosește Adevărul pentru a face afirmații pe mesajul de excepție:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
Avantajele față de abordările în alte răspunsuri sunt:
aruncă
clauzaO metodă similară va fi adăugat la `org.junit Afirme în JUnit 4.13.
Ce zici de asta: Prinde o excepție generală, asigurați-vă că face din bloc catch, apoi afirma că clasa de excepție este ceea ce te aștepți să fie. Acest afirme va eșua în cazul a) excepția este de tip greșit (de exemplu. dacă ai un Pointer Null în loc) și b) excepția a fost't mai aruncat.
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
import static com.googlecode.catchexception.apis.BDDCatchException.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(() -> foo.doStuff());
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
eu.codearte.catch-exception:catch-exception:2.0
Folosind o AssertJ afirmație, care poate fi folosit alături de JUnit:
import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
L's mai mult de @Test(expected=IndexOutOfBoundsException.class)
deoarece garantează așteptat linie de testare a aruncat o excepție și vă permite să verificați mai multe detalii despre extensia de excepție, cum ar fi mesaje, mai ușor:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Pentru a rezolva aceeași problemă am facut un mic proiect: http://code.google.com/p/catch-exception/
Folosind acest mic ajutor le-ar scrie
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
Acest lucru este mai puțin detaliată decât ExpectedException statului de JUnit 4.7. În comparație cu soluția prevăzută de skaffman, puteți specifica în care linia de cod te astepti de excepție. Sper că acest lucru ajută.
Actualizare: JUnit5 a o îmbunătățire pentru excepții de testare: assertThrows
.
următorul exemplu este de: Junit 5 Ghidul Utilizatorului
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Răspuns inițial folosind JUnit 4.
Există mai multe moduri de a testa care este aruncată o excepție. Am discutat, de asemenea, opțiunile de mai jos in postul meu Cum pentru a scrie teste unitare cu JUnit
Setați așteptat
parametru @Test(temperatura = FileNotFoundException.class)
.
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Folosind "încerca ""prinde"
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!"));
}
}
Testarea cu ExpectedException Regula.
@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");
}
Ati putea citi mai multe despre excepții, testări în JUnit4 wiki de Excepție testarea și rea.robot - Așteaptă Excepții JUnit Regula.
IMHO, cel mai bun mod de a verifica pentru excepții în JUnit este try/catch/nu/afirma model:
// 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());
}
Anii assertTrue
ar putea fi un pic prea puternic pentru unele persoane, așa assertThat(e.getMessage(), containsString("mesaj"); ar fi de preferat.
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
Mai multe Informatii despre JUnit 5 pe http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
Am incercat multe din metodele de aici, dar ele au fost fie complicat sau nu't destul îndeplinească cerințele mele. În fapt, se poate scrie un helper metoda destul de simpla:
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;
}
}
Utilizați ca aceasta:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
Zero dependențe: nu este nevoie pentru mockito, nu este nevoie powermock; și funcționează foarte bine cu final clase.
Cel mai flexibil și elegant răspunsul pentru Junit 4-am găsit în Mkyong blog. Are flexibilitatea de a try/catch
ajutorul @Regula
adnotare. Îmi place această abordare, deoarece puteți citi atribute specifice unui personalizate excepție.
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("");
}
}
Dacă doriți o soluție care:
Aici este o funcție de utilitate care l-am scris:
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.
}
Utilizați după cum urmează:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
În cazul meu, am obține întotdeauna RuntimeException de db, dar mesajele diferă. Și excepție trebuie să fie manipulate respectiv. Aici este modul în care l-am testat:
@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));
}