JUnit 4 testlerinde belirli bir istisnanın atıldığını nasıl iddia ediyorsunuz?


1999

Bazı kodların bir istisna atıp atmadığını sınamak için JUnit4'ü deyimsel olarak nasıl kullanabilirim?

Kesinlikle böyle bir şey yapabilirken:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

Bu tür durumlar için bir açıklama ya da bir Assert.xyz ya da çok daha az kurnaz ve JUnit'in ruhunda çok daha fazla olan bir şey olduğunu hatırlıyorum .


21
Başka herhangi bir yaklaşımla ilgili sorun, ancak istisna atıldıktan sonra her zaman testi sonlandırmalarıdır. Öte yandan, org.mockito.Mockito.verifyistisna atılmadan önce genellikle bazı şeylerin (bir günlükçü hizmetinin doğru parametrelerle çağrıldığı şekilde) gerçekleştiğinden emin olmak için hala çeşitli parametrelerle aramak istiyorum .
ZeroOne

5
İstisna testinin nasıl yapılacağını JUnit wiki sayfasında görebilirsiniz github.com/junit-team/junit/wiki/Exception-testing
PhoneixS

6
@ZeroOne - Bunun için biri istisna için diğeri de alayınızla etkileşimi doğrulamak için iki farklı testim olacaktı.
tddmonkey

Bunu JUnit 5 ile yapmanın bir yolu var, cevabımı aşağıda güncelledim.
Dilini Rajapaksha

Yanıtlar:


2363

Bu, JUnit sürümüne ve kullandığınız onay kütüphanelerine bağlıdır.

Orijinal cevap JUnit <= 4.12şuydu:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

Her ne kadar cevap https://stackoverflow.com/a/31826781/2986984 , JUnit <= 4.12 için daha fazla seçeneğe sahiptir.

Referans :


66
Kodunuzda sadece bir yerde bir istisna bekliyorsanız ve bu tür bir battaniye değil, bu kod parçası çalışmaz.
Oh Chin Boon

4
@skaffman org.junit.experimental.theories.Theory ile bu olmaz iş org.junit.experimental.theories.Theories koşucu
Artem Oboturov

74
Roy Osherove , Ünite Testi sanatında bu tür İstisna testlerinden vazgeçirir , çünkü İstisna sadece test edilen birimin içinde değil, testin herhangi bir yerinde olabilir.
Kevin Wittek

21
@ Kiview / Roy Osherove ile aynı fikirde değilim. Bana göre, testler uygulama için değil davranış için olmalıdır. Belirli bir yöntemin hata verebileceğini test ederek, testlerinizi doğrudan uygulamaya bağlamış olursunuz. Yukarıda gösterilen yöntemde test etmenin daha değerli bir test sağladığını iddia ediyorum. Ekleyeceğim uyarı, bu durumda özel bir istisnayı test edeceğim, böylece gerçekten istediğim istisnayı aldığımı biliyorum.
nickbdyer

6
Ne. Sınıfın davranışını test etmek istiyorum. Önemli olan, orada olmayan bir şeyi almaya çalışırsam, bir istisna alırım. Veri yapısının ArrayListyanıt vermesi get()önemsizdir. Gelecekte ilkel bir diziye geçmeyi seçtiysem, bu test uygulamasını değiştirmem gerekirdi. Veri yapısı gizlenmelidir, böylece test sınıfın davranışına odaklanabilir .
nickbdyer

1316

Düzenleme: Şimdi JUnit 5 ve JUnit 4.13 yayınlandı, en iyi seçenek Assertions.assertThrows() (JUnit 5 için) ve Assert.assertThrows()(JUnit 4.13 için) kullanmak olacaktır. Ayrıntılar için diğer cevabıma bakın.

JUnit 5'e geçmediyseniz ancak JUnit 4.7'yi kullanabiliyorsanız, ExpectedExceptionKuralı kullanabilirsiniz :

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

Bu çok daha iyidir @Test(expected=IndexOutOfBoundsException.class)çünkü daha IndexOutOfBoundsExceptionönce atılırsa test başarısız olurfoo.doStuff()

Ayrıntılar için bu makaleye bakın


14
@skaffman - Bunu doğru anladıysam, istisna gibi görünüyor. beklenti tüm sınıfta değil, sadece bir testte uygulanıyor.
bacar

5
Atılmasını beklediğimiz istisna kontrol edilmiş bir istisnaysa, atışları eklemeli miyiz veya bu durumu başka bir şekilde test etmeli miyiz?
Mohammad Jafar Mashhadi

5
@MartinTrummer Kural dışı durum atıldığından ve yöntemden çıkıldığından foo.doStuff () öğesinden sonra hiçbir kod çalıştırılmamalıdır. Beklenen bir kural dışı durumdan sonra koda sahip olmak (kaynakların son olarak kapatılması hariç) yine de yararsızdır, çünkü kural dışı durum atılırsa asla yürütülmemelidir.
Jason Thompson

9
Bu en iyi yaklaşım. Burada skaffman'ın çözümüne kıyasla iki avantajı var. İlk olarak, ExpectedExceptionsınıfın istisnanın mesajını eşleştirme, hatta istisna sınıfına bağlı kendi eşleştiricinizi yazma yöntemleri vardır. İkincisi, beklentinizi istisnayı atmayı beklediğiniz kod satırından hemen önce ayarlayabilirsiniz - bu, yanlış kod satırı istisnayı atarsa ​​testinizin başarısız olacağı anlamına gelir; oysa bunu skaffman'ın çözümü ile yapmanın bir yolu yok.
Dawood ibn Kareem

5
@MJafarMash atmayı beklediğiniz istisna işaretlenirse, bu istisnayı test yönteminin throws yan tümcesine eklersiniz. Belirli bir test durumunda istisna tetiklenmese bile, kontrol edilen bir istisnayı attığı bildirilen bir yöntemi test ettiğinizde de aynısını yaparsınız.
NamshubWriter

472

Beklenen istisnayı kullanırken dikkatli olun, çünkü yalnızca yöntemin testte belirli bir kod satırı değil, bu istisnayı attığını iddia eder .

Bu tür yöntemler genellikle çok basit, ancak daha karmaşık testler daha iyi sunulabilir, çünkü parametre doğrulama test etmek için bu eğilimindedir:

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

Yargı uygula.


95
Belki eski okulum ama yine de bunu tercih ediyorum. Ayrıca bana istisnanın kendisini test etmek için bir yer verir: bazen belirli değerler için getters ile istisnalar var, ya da sadece mesajda belirli bir değer arayabilir (örneğin "tanınmayan kod 'xyz" mesajında ​​"xyz" araması yapabilirim) ").
Rodney Gitzel

3
NamshubWriter'ın yaklaşımı size her iki dünyanın en iyisini sunuyor.
Eddie

4
ExpectedException kullanarak bu exception.expect (IndexOutOfBoundsException.class) gibi test etmek için yöntem başına N exception.expect çağırabilirsiniz; foo.doStuff1 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff2 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff3 ();
user1154664

10
@ user1154664 Aslında yapamazsınız. ExpectedException kullanarak yalnızca bir yöntemin bir istisna attığını test edebilirsiniz, çünkü bu yöntem çağrıldığında, beklenen istisnayı attığı için test yürütmeyi durduracaktır!
NamshubWriter

2
İlk cümleniz doğru değil. Kullanırken ExpectedExceptionyapılacak normal şey, beklentiyi istisnayı atmayı beklediğiniz çizgiden hemen önce ayarlamaktır. Bu şekilde, daha önceki bir satır istisnayı atarsa, kuralı tetiklemez ve test başarısız olur.
Dawood ibn Kareem

213

Daha önce yanıtlandığı gibi, JUnit'te istisnalarla başa çıkmanın birçok yolu vardır. Ancak Java 8 ile bir tane daha var: Lambda Expressions kullanma. Lambda İfadeleri ile şöyle bir sözdizimi elde edebiliriz:

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

assertThrown, örnekleri lambda ifadeleri, yöntem başvuruları veya yapıcı başvuruları ile oluşturulabilen işlevsel bir arabirimi kabul eder. assertThwn bu arayüzü kabul ederek bir istisna bekler ve hazır olmaya hazırdır.

Bu nispeten basit ama güçlü bir tekniktir.

Bu tekniği anlatan bu blog yayınına bir göz atın: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

Kaynak kodu şu adreste bulunabilir: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

Açıklama: Blogun ve projenin yazarıyım.


2
Bu çözümü beğendim ama bunu bir maven deposundan indirebilir miyim?
Selwyn

@Maven'de bulunan bu fikrin bir uygulaması stefanbirkner.github.io/vallado
NamshubWriter

6
@CristianoFontes, JUnit 4.13 için bu API'nın daha basit bir sürümünün yerini alır. Bkz. Github.com/junit-team/junit/commit/…
NamshubWriter

@RafalBorowiec teknik new DummyService()::someMethodolarak bir MethodHandle, ancak bu yaklaşım lambda ifadeleri ile eşit derecede iyi çalışır.
Andy

@NamshubWriter, Görünüşe göre Junit 4.13 Junit 5 lehine terk edildi: stackoverflow.com/questions/156503/…
Vadzim

154

junit'te, istisnayı test etmenin dört yolu vardır.

junit5.x

  • junit5.x için assertThrowsaşağıdaki gibi kullanabilirsiniz

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

junit4.x

  • junit4.x için, Test bilgi notunun isteğe bağlı 'beklenen' özelliğini kullanın

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
  • junit4.x için ExpectedException kuralını kullanın

    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();
        }
    }
  • ayrıca junit 3 çerçevesi altında yaygın olarak kullanılan klasik try / catch yolunu kullanabilirsiniz

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

    • Eğer haziran 5'i seviyorsanız, ilkini beğenmelisiniz
    • yalnızca istisna türünü test etmek istediğinizde 2. yol kullanılır
    • ilk ve son ikisi istisna mesajı daha fazla test etmek istediğinizde kullanılır
    • Eğer haziran 3'ü kullanırsanız, dördüncüsü tercih edilir
  • daha fazla bilgi için, bu belgeyi ve junit5 kullanıcı kılavuzunu ayrıntılar için okuyabilirsiniz .


6
Benim için bu en iyi cevap, tüm yolları çok açık bir şekilde kapsıyor, teşekkürler! Şahsen okunabilirlik için Junit4 ile bile 3. seçeneği kullanmaya devam ediyorum, boş yakalama bloğundan kaçınmak için Atılabilir ve iddia edilen e türünü de yakalayabilirsiniz
Nicolas Cornette

İşaretli özel durum beklemek için ExpectedException kullanmak mümkün mü?
miuser

Tek şey, ilk üç cevabın birikimidir. IMO, yeni bir şey eklemiyorsa bu cevap gönderilmemeliydi. Sadece cevap için (popüler bir soru) temsilcisi. Oldukça işe yaramaz.
Paul Samsotha

emin, çünkü Trowableyöntemden türetilmiş herhangi bir tür iletebilirsiniz ExpectedException.expect. lütfen onun imzasına bakın . @miuser
walsh

116

tl; Dr.

  • JDK8 sonrası: Olağanüstü davranışlar öne sürmek için AssertJ veya özel lambdas kullanın .

  • Önceden JDK8: Eski iyi tavsiye eder try- catchbloğu. ( Bloktan önce bir fail()iddia eklemeyi unutmayıncatch )

Junit 4 veya JUnit 5'ten bağımsız olarak.

uzun hikaye

Kendinize bir kendiniz yapın try - catchJUnit araçlarını ( @Test(expected = ...)veya @Rule ExpectedExceptionJUnit kuralı özelliğini) engelleyin veya kullanın .

Ancak bu yollar çok zarif değildir ve okunabilirliği akıllıca diğer araçlarla karıştırmaz . Ayrıca, JUnit takım bazı tuzaklar vardır.

  1. try- catchEğer test davranışa geçici blok yazma ve catch bloğu içinde iddiasını yazmak zorunda blok, sorun ancak birçok bulmak olabilir bu tarz kesmeler testin okuma akışını söyledi. Ayrıca, bloğun Assert.failsonuna bir de yazmanız gerekir try. Aksi takdirde, test iddiaların bir tarafını kaçırabilir; PMD , findbugs veya Sonar bu tür sorunları tespit edecek.

  2. Bu @Test(expected = ...)özellik daha az kod yazabileceğiniz ve daha sonra bu testi yazarken kodlama hatalarına daha az eğilimli olduğu için ilginçtir. Ancak bu yaklaşım bazı alanlarda eksiktir.

    • Testin, istisna veya mesaj gibi istisnadaki ek şeyleri kontrol etmesi gerekiyorsa (iyi istisna mesajları gerçekten önemlidir, kesin bir istisna türüne sahip olmak yeterli olmayabilir).
    • Beklenti yönteminde etrafına yerleştirilir olarak da, test kod sonra yazılı şekline bağlı testi kodunun yanlış bölümü yanlış pozitif teste yol istisna atabilir ve emin değilim o PMD , findbugs veya Sonar bu kod hakkında ipuçları verecektir.

      @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. ExpectedExceptionKural önceki uyarılar düzeltmek için bir girişim, ama bir o bir beklenti stili kullanır olarak kullanmak garip bit hissediyor EasyMock kullanıcıları çok iyi bu tarzı biliyorum. Bazıları için uygun olabilir, ancak Davranış Odaklı Gelişim (BDD) veya Arrange Act Assert (AAA) ilkelerini uygularsanız , ExpectedExceptionkural bu yazma stiline uymaz. Bunun yanı sıra @Test, beklentiyi nereye yerleştirdiğinize bağlı olarak, yolla aynı sorundan muzdarip olabilir .

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

    Beklenen istisna bile test ifadesinin önüne yerleştirilir, eğer testler BDD veya AAA'yı takip ediyorsa okuma akışınızı keser.

    Ayrıca, yazarının JUnit ile ilgili bu yorum sorununa bakın ExpectedException. JUnit 4.13-beta-2 bile bu mekanizmayı reddetmektedir:

    Çekme isteği # 1519 : Beklenen İstisnadan Vazgeç

    Assert.assertThrows yöntemi, özel durumları doğrulamak için daha iyi bir yol sağlar. Buna ek olarak, TestWatcher gibi diğer kurallarla birlikte kullanıldığında ExpectedException kullanımı hataya açıktır, çünkü bu durumda kuralların sırası önemlidir.

Dolayısıyla bu yukarıdaki seçenekler tüm uyarılar yüküne sahiptir ve açıkça kodlayıcı hatalarına karşı bağışık değildir.

  1. Bu cevabı oluşturduktan sonra umut verici görünen bir proje var, bu bir istisna .

    Projenin açıklamasının dediği gibi, bir kodlayıcının istisnayı yakalayan akıcı bir kod satırı yazmasına izin verir ve ikinci iddia için bu istisnayı sunar. Ve Hamcrest veya AssertJ gibi herhangi bir iddia kütüphanesini kullanabilirsiniz .

    Ana sayfadan alınan hızlı bir örnek:

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

    Kodun gerçekten basit olduğunu görebildiğiniz gibi, istisnayı belirli bir satırda yakalarsınız, thenAPI AssertJ API'larını (kullanıma benzer assertThat(ex).hasNoCause()...) kullanacak bir takma addır . Proje bir noktada AssertJ'nin atası FEST-Assert'e güveniyordu . EDIT: Görünüşe göre proje bir Java 8 Lambdas desteği hazırlıyor.

    Şu anda bu kütüphanede iki eksiklik var:

    • Bu yazı yazıldığı sırada, bu kütüphanenin Mockito 1.x'e dayandığını söylemek önemlidir, çünkü sahnenin arkasında test edilen nesnenin bir kısmını oluşturur. Mockito hala güncellenmediğinden, bu kütüphane son sınıflarla veya son yöntemlerle çalışamaz . Ve mevcut sürümde Mockito 2'ye dayanmış olsa bile, bu inline-mock-maker, sahte bir normal sahte yapımcıdan farklı dezavantajlara sahip olduğundan, istediğiniz bir şey olmayan bir küresel sahte yapımcı ( ) beyan etmek gerekir .

    • Yine başka bir test bağımlılığı gerektirir.

    Bu sorunlar kütüphane lambdaları desteklediğinde geçerli olmaz. Ancak, işlevsellik AssertJ araç seti tarafından çoğaltılacaktır.

    Catch-istisna aracını kullanmak istemiyorsanız, tüm bunları göz önünde bulundurarak , en azından JDK7'ye kadar try- catchbloğun eski iyi yolunu önereceğim. Ve JDK 8 kullanıcıları için, sadece istisnalar öne sürmekten daha fazlasını sunabileceği için AssertJ'i kullanmayı tercih edebilirsiniz.

  2. JDK8 ile lambdalar test sahnesine girerler ve olağanüstü davranışlar sergilemenin ilginç bir yolu olduğunu kanıtladılar. AssertJ, istisnai davranışlar öne sürmek için iyi bir akıcı API sağlayacak şekilde güncellendi.

    Ve AssertJ ile bir örnek test :

    @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. JUnit 5'in neredeyse tamamen yeniden yazılmasıyla, iddialar biraz geliştirildi , uygun istisnayı iddia etmenin kutudan çıktığı bir yol olarak ilginç olabilirler. Ama gerçekten iddia API'sı hala zayıf, dışarıda hiçbir şey yok assertThrows.

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

    Fark ettiğiniz gibi assertEqualshala geri dönüyor voidve bu nedenle AssertJ gibi zincirleme iddialara izin vermiyor.

    Ayrıca ad çakışmasını hatırlıyorsanız Matcherveya Assert, aynı çatışmayı karşılamaya hazır olun Assertions.

Bugün (2017-03-03) AssertJ'in kullanım kolaylığı, keşfedilebilir API, hızlı geliştirme hızı ve fiili test bağımlılığı olarak test çerçevesinden (JUnit) bağımsız olarak JDK8 ile en iyi çözüm olduğu sonucuna varmak istiyorum. ya da olmasın), önceki JDKs yerine güvenmek gerekir try-catch onlar aksak hissetseniz bile blokları.

Bu cevap, aynı görünürlüğe sahip olmayan başka bir sorudan kopyalandı , ben aynı yazarım.


1
AssertThrows'u kullanabilmek için org.junit.jupiter: junit-jupiter-engine: 5.0.0-RC2 bağımlılığı (zaten mevcut junit: junit: 4.12'ye ek olarak) eklemek belki tercih edilen bir çözüm değildir, ancak benim için sorunları.
anre

Ben ExpectedException kuralını kullanma hayranıyım ama her zaman AAA ile kırar beni rahatsız etti. Tüm farklı yaklaşımları tanımlamak için mükemmel bir makale yazdınız ve beni kesinlikle AssertJ'i denemeye teşvik ettiniz :-) Teşekkürler!
Pim Hazebroek

@PimHazebroek teşekkürler. AssertJ API oldukça zengindir. Bence JUnit'in kutudan çıkardığı şey daha iyi.
Brice

64

JUnit 5 ve JUnit 4.13 yayınlandığına göre, en iyi seçenek Assertions.assertThrows() (JUnit 5 için) ve Assert.assertThrows()(JUnit 4.13 için) kullanmak olacaktır. Bkz JUnit 5 Kullanıcı Kılavuzu .

Kural dışı durumun atıldığını doğrulayan ve kural dışı durum iletisinde iddialarda bulunmak için Gerçeği kullanan bir örnek :

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

Diğer cevaplardaki yaklaşımlara göre avantajları:

  1. JUnit'te yerleşik
  2. Lambda'daki kod bir istisna atmazsa yararlı bir istisna mesajı ve farklı bir istisna atarsa ​​bir yığın izlemesi alırsınız
  3. Özlü
  4. Testlerinizin Arrange-Act-Assert'i takip etmesine izin verir
  5. İstisnayı atmayı beklediğiniz kodu tam olarak belirtebilirsiniz
  6. Sen beklenen istisna listelemek gerekmez throwsmaddesi
  7. Yakalanan istisna hakkında iddialarda bulunmak için seçtiğiniz onaylama çerçevesini kullanabilirsiniz

Benzer bir yöntem org.junit AssertJUnit 4.13'te eklenecektir .


Bu yaklaşım temiz, ama testin "Düzenleme-Eylem-Assert" i takip etmesine nasıl izin verdiğini görmüyorum, çünkü "Eylem" bölümünü bir "assertThrow" a çevirmek zorundayız.
Clockwork

@ Clockwork Lambda "hareket" tir. Arrange-Act-Assert'in amacı, kodu temiz ve basit hale getirmektir (ve bu nedenle anlaşılması ve bakımı kolaydır). Belirttiğiniz gibi, bu yaklaşım temizdir.
NamshubWriter

Yine de testin sonunda atmayı ve istisnayı "iddia" bölümünde iddia edebileceğimi umuyordum. Bu yaklaşımda, önce onu yakalamak için eylemi bir ilk iddiaya sarmanız gerekir.
Clockwork

Bu, iddiayı yapmak için her testte daha fazla kod gerektirir. Bu daha fazla kod ve hataya açık olur.
NamshubWriter

43

Buna ne dersiniz: Çok genel bir istisna yakalayın, onu catch bloğundan çıkardığından emin olun, sonra istisnanın sınıfının olmasını beklediğiniz şey olduğunu iddia edin. A) istisna yanlış tipteyse (örn. Bunun yerine bir Boş İşaretçi varsa) ve b) istisna hiç atılmamışsa bu iddia başarısız olur.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}

3
Ayrıca, testin başarısız olduğu gün geldiğinde test sonuçlarında ne tür Exception ex olduğunu göremezsiniz.
jontejj

Sonunda iddia ettiğiniz şekli değiştirerek bu biraz geliştirilebilir. assertEquals(ExpectedException.class, e.getClass())test başarısız olduğunda beklenen ve gerçek değerleri gösterecektir.
Cypher

37

BDD Stil Çözümü: JUnit 4 + Catch Exception + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

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

}

Bağımlılıklar

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

36

JUnit ile birlikte kullanılabilen bir AssertJ iddiası kullanarak :

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

@Test(expected=IndexOutOfBoundsException.class)Testte beklenen satırın istisnayı atmasını garanti etmesinden ve istisna hakkında mesaj gibi daha fazla ayrıntıyı daha kolay kontrol etmenize izin vermesinden daha iyidir :

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

Maven / Gradle talimatları burada.


en özlü yolu ve kimse takdir, garip .. assertJ kütüphanesi ile ilgili sadece bir sorun var, assertHunit ile isimle çatışır. assertJ throwby: JUnit: Java 8 ve AssertJ 3.0 ile İstisnaları Test Etme
ycomp

@ycomp Çok eski bir soruya yeni bir cevap, bu yüzden puan farkı aldatıcı.
weston

Java 8 ve AssertJ kullanabiliyorsa, bu muhtemelen en iyi çözümdür!
Pierre Henry

@ycomp Bu ad çakışmasının tasarımdan kaynaklanabileceğinden şüpheleniyorum: AssertJ kütüphanesi, JUnit'i assertThat, her zaman AssertJ olanı kullanmamanızı şiddetle teşvik eder . Ayrıca, JUnit yöntemi yalnızca "normal" bir tür döndürürken AssertJ yöntemi AbstractAssert, yukarıdaki gibi yöntemlerin dizilmesine izin veren bir alt sınıf döndürür (veya bunun için teknik terimler ne olursa olsun ...).
mike kemirgen

@weston aslında tekniğinizi AssertJ 2.0.0'da kullandım. Yükseltme için hiçbir mazeret, şüphesiz, ama bilmek isteyebilirsiniz.
mike kemirgen

33

Aynı sorunu çözmek için küçük bir proje kurdum: http://code.google.com/p/catch-exception/

Bu küçük yardımcıyı kullanarak

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

Bu, JUnit 4.7'nin ExpectedException kuralından daha az ayrıntılıdır. Skaffman tarafından sağlanan çözümle karşılaştırıldığında, istisnayı hangi kod satırında beklediğinizi belirtebilirsiniz. Umarım bu yardımcı olur.


Ben de böyle bir şey yapmayı düşündüm, ama sonuçta ExpectedException'ın gerçek gücünün sadece beklenen istisnayı belirtmekle kalmayıp, aynı zamanda beklenen neden veya beklenen mesaj gibi istisnanın belirli özelliklerini de belirleyebileceğini keşfettim.
Jason Thompson

Benim tahminim bu çözüm alaylarla aynı dezavantajlara sahip mi? Örneğin, eğer fooolduğunu finalbaşarısız olur proxy olamaz çünkü foo?
Tom

Tom, doStuff () bir arayüzün parçasıysa proxy yaklaşımı çalışır. Aksi takdirde bu yaklaşım başarısız olur, haklısın.
rwitzel

31

Güncelleme: JUnit5 test istisnalar bir iyileşme vardır: assertThrows.

aşağıdaki örnek: Junit 5 Kullanıcı Kılavuzu

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

JUnit 4 kullanarak orijinal cevap.

Bir istisnanın atıldığını test etmenin birkaç yolu vardır. Ayrıca yazıma aşağıdaki seçenekleri de tartıştım JUnit ile harika birim testleri nasıl yazılır

expectedParametreyi ayarlayın @Test(expected = FileNotFoundException.class).

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

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

}

ExpectedExceptionKural ile Test .

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

İstisna testi için istisna testi ve istisna testi için bad.robot istisna testi JUnit Kuralı hakkında daha fazla bilgi edinebilirsiniz .


22

Bunu da yapabilirsiniz:

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

12
JUnit testlerde, bu daha iyi kullanmak için var Assert.fail(), değil assert, testlerin bir ortamda çalışacak her ihtimale karşı iddialar etkin değildir nerede.
NamshubWriter

14

IMHO, JUnit'teki istisnaları kontrol etmenin en iyi yolu try / catch / fail / assert örüntüsüdür:

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

assertTrueBazı insanlar için biraz güçlü olabilir, bu nedenle assertThat(e.getMessage(), containsString("the message");tercih olabilir.



13

Junk 4 için en esnek ve zarif cevap Mkyong blogunda buldum . Ek açıklamayı try/catchkullanma esnekliğine sahiptir @Rule. Bu yaklaşımı seviyorum çünkü özelleştirilmiş bir istisnanın belirli özelliklerini okuyabilirsiniz.

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

    }

}

12

Buradaki yöntemlerin çoğunu denedim, ancak ya karmaşıktı ya da gereksinimlerimi tam olarak karşılamadı. Aslında, bir yardımcı yöntem oldukça basit bir şekilde yazılabilir:

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

Şöyle kullanın:

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

Sıfır bağımlılıklar: mockito'ya gerek yok, powermock'a gerek yok; ve son sınıflarla gayet iyi çalışıyor.


İlginçtir, ancak Act ve Assert adımını aslında farklı adımlarla yapmak istediğiniz AAA'ya (Act Assert'i Düzenle) uymaz.
bln-tom

1
@ bln-tom Teknik olarak iki farklı adım, sadece bu sırada değiller. ; p
Trejkaz

10

Java 8 çözümü

Aşağıdakileri içeren bir çözüm istiyorsanız:

  • Java 8 lambdas'ı kullanır
  • does not herhangi JUnit büyü bağlıdır
  • Tek bir test yönteminde birden fazla istisna olup olmadığını kontrol etmenizi sağlar
  • Tüm test yöntemindeki bilinmeyen bir satır yerine, test yönteminizdeki belirli bir satır kümesi tarafından atılan bir istisna olup olmadığını kontrol eder
  • Daha fazla inceleyebilmeniz için atılan gerçek istisna nesnesini verir

İşte yazdığım bir yardımcı program işlevi:

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

( blogumdan alınmıştır )

Aşağıdaki gibi kullanın:

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


8

Benim durumumda her zaman db RuntimeException olsun, ancak mesajlar farklı. Ve istisnaların sırasıyla ele alınması gerekir. İşte nasıl test:

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

1
önce çizgi } catch (eklemelisinizfail("no exception thrown");
Daniel Alder

6

Sadece kapatılabilecek ve açılabilecek bir Eşleştirici yapın, şöyle:

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

Kullanmak için:

ekleyin public ExpectedException exception = ExpectedException.none();, sonra:

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

6

JUnit 4 veya sonraki sürümlerde istisnaları aşağıdaki gibi test edebilirsiniz

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


bu, JUnit testlerimizi geliştirmek için kullanılabilecek birçok özellik sunar.
Aşağıdaki örneği görürseniz, istisnada 3 şeyi test ediyorum.

  1. Atılan istisna türü
  2. İstisna Mesajı
  3. İstisnanın nedeni


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

}

6

İstisna döndürmesi gereken yöntemden sonra bir onaylama hatası kullanabiliriz:

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

3
İkincisi catch, başka bir istisna atılırsa yığın izini yutar ve yararlı bilgiler kaybeder
NamshubWriter

5

NamShubWriter'ın söylediklerine ek olarak , şunlardan emin olun:

  • ExpectedException örneği herkese açık ( İlgili Soru )
  • ExpectedException değil , söz hakkından içinde @Before yöntemi örneği. Bu yazı , JUnit'in infaz düzeninin tüm karmaşıklıklarını açıkça açıklıyor.

Do not bunu:

@Rule    
public ExpectedException expectedException;

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

Son olarak, bu blog yazısı, belirli bir istisnanın atıldığını nasıl iddia edileceğini açıkça göstermektedir.


4

Kütüphaneyi tavsiye ederim assertj-coreJunit testinde istisnayı ele almak için

Java 8'de şöyle:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);

2

Java8 ile Junit4 çözümü bu işlevi kullanmaktır:

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

Kullanım o zaman:

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

Tek sınırlamanın finallambda ifadesinde bir nesne başvurusu kullanmak olduğunu unutmayın . Bu çözelti, @Test(expected = IndexOutOfBoundsException.class)çözelti kullanılarak yöntem seviyesinde çözülebilir olmasını beklemek yerine test iddialarına devam etmeyi sağlar .


1

Örneğin, aşağıda belirtilen kod parçası için Junit yazmak istiyorsunuz

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

Yukarıdaki kod, oluşabilecek bazı bilinmeyen istisnaları test etmek ve aşağıda özel mesajla bazı istisnaları bildirmektir.

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

1
    @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;

               }

    }

İşte doğru istisna atılan yöntemi kontrol etmek için başka bir yol.


1

JUnit framework assertThrows()yöntemi vardır:

ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
    calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());

0

Java 8 ile kontrol etmek için bir kod ve beklenen istisnayı parametre olarak alarak bir yöntem oluşturabilirsiniz:

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

ve sonra testinizin içinde:

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

Yararları:

  • herhangi bir kütüphaneye dayanmamak
  • yerelleştirilmiş kontrol - daha kesin ve gerekirse tek bir testte böyle birden fazla iddiaya sahip olmanızı sağlar
  • kullanımı kolay
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.