คุณยืนยันว่ามีข้อยกเว้นบางอย่างเกิดขึ้นในการทดสอบ JUnit 4 ได้อย่างไร


1999

ฉันจะใช้ JUnit4 โดยใช้สำนวนเพื่อทดสอบว่าบางรหัสผิดพลาดได้อย่างไร

ในขณะที่ฉันสามารถทำอะไรเช่นนี้:

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

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

  assertTrue(thrown);
}

ฉันจำได้ว่ามีคำอธิบายประกอบหรือ Assert.xyz หรือบางสิ่งที่มีความบกพร่องน้อยกว่าและมีจิตวิญญาณของ JUnit มากขึ้นสำหรับสถานการณ์ประเภทนี้


21
ปัญหาเกี่ยวกับวิธีการอื่น ๆ แต่นี่คือพวกเขามักจะจบการทดสอบเมื่อข้อยกเว้นได้ถูกโยน ในทางกลับกันฉันมักจะต้องการโทรorg.mockito.Mockito.verifyด้วยพารามิเตอร์ต่าง ๆ เพื่อให้แน่ใจว่าสิ่งต่าง ๆ เกิดขึ้น (เช่นบริการตัวบันทึกถูกเรียกด้วยพารามิเตอร์ที่ถูกต้อง) ก่อนที่จะถูกโยนทิ้ง
ZeroOne

5
คุณสามารถดูวิธีทดสอบข้อยกเว้นในหน้า JUnit wiki github.com/junit-team/junit/wiki/Exception-testing
PhoneixS

6
@ ZeroOne - สำหรับฉันจะมีการทดสอบสองแบบที่แตกต่างกัน - หนึ่งข้อยกเว้นและอีกข้อหนึ่งเพื่อยืนยันการโต้ตอบกับการเยาะเย้ยของคุณ
tddmonkey

มีวิธีในการทำเช่นนี้กับ JUnit 5 ฉันได้อัปเดตคำตอบของฉันด้านล่าง
Dilini Rajapaksha

คำตอบ:


2363

มันขึ้นอยู่กับรุ่น JUnit และสิ่งที่ยืนยันห้องสมุดที่คุณใช้

คำตอบเดิมสำหรับJUnit <= 4.12คือ:

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

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

}

แม้ว่าคำตอบhttps://stackoverflow.com/a/31826781/2986984มีตัวเลือกเพิ่มเติมสำหรับ JUnit <= 4.12

เอกสารอ้างอิง:


66
รหัสชิ้นนี้จะไม่ทำงานหากคุณคาดว่าจะมีข้อยกเว้นเพียงบางส่วนในรหัสของคุณและไม่ครอบคลุมเช่นนี้
Oh Chin Boon

4
@skaffman สิ่งนี้จะใช้ไม่ได้กับ org.junit.experimental.theories.The ที่ดำเนินการโดย org.junit.experimental.theories.Theories
Artem Oboturov

74
Roy Osherove ไม่สนับสนุนการทดสอบข้อยกเว้นชนิดนี้ในศิลปะของการทดสอบหน่วยเนื่องจากข้อยกเว้นอาจอยู่ที่ใดก็ได้ในการทดสอบและไม่เพียง แต่อยู่ในหน่วยการทดสอบ
Kevin Wittek

21
ฉันไม่เห็นด้วยกับ @ Kiview / Roy Osherove ในมุมมองของฉันการทดสอบควรมีเพื่อพฤติกรรมไม่ใช่การนำไปใช้ โดยการทดสอบว่าวิธีการเฉพาะสามารถทำให้เกิดข้อผิดพลาดได้คุณกำลังทดสอบการใช้งานของคุณโดยตรง ฉันจะยืนยันว่าการทดสอบในวิธีที่แสดงข้างต้นให้การทดสอบที่มีค่ามากกว่า ข้อแม้ที่ฉันจะเพิ่มคือในกรณีนี้ฉันจะทดสอบข้อยกเว้นที่กำหนดเองเพื่อให้ฉันรู้ว่าฉันได้รับข้อยกเว้นที่ฉันต้องการจริงๆ
nickbdyer

6
ทั้ง ฉันต้องการทดสอบพฤติกรรมของชั้นเรียน สิ่งสำคัญคือถ้าฉันพยายามเรียกคืนบางสิ่งที่ไม่มีฉันจะได้รับการยกเว้น ความจริงที่ว่าโครงสร้างข้อมูลคือการArrayListตอบสนองที่get()ไม่เกี่ยวข้อง ถ้าฉันเลือกในอนาคตเพื่อย้ายไปยังอาร์เรย์ดั้งเดิมฉันจะต้องเปลี่ยนการใช้งานการทดสอบนี้ โครงสร้างข้อมูลที่ควรจะซ่อนเพื่อให้การทดสอบสามารถมุ่งเน้นไปที่พฤติกรรมของชั้น
nickbdyer

1316

แก้ไข:ตอนนี้ปล่อย JUnit 5 และ JUnit 4.13 ตัวเลือกที่ดีที่สุดคือการใช้Assertions.assertThrows() (สำหรับ JUnit 5) และAssert.assertThrows()(สำหรับ JUnit 4.13) ดูคำตอบอื่น ๆ ของฉันสำหรับรายละเอียด

หากคุณไม่ได้ย้ายไปยัง JUnit 5 แต่สามารถใช้ JUnit 4.7 คุณสามารถใช้ExpectedExceptionกฎได้:

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

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

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

นี้ดีกว่า@Test(expected=IndexOutOfBoundsException.class)เพราะการทดสอบจะล้มเหลวหากIndexOutOfBoundsExceptionถูกโยนก่อนfoo.doStuff()

ดูบทความนี้สำหรับรายละเอียด


14
@skaffman - หากฉันเข้าใจอย่างถูกต้องดูเหมือนว่า exception.expect จะถูกใช้ภายในการทดสอบเพียงครั้งเดียวไม่ใช่ทั้งชั้นเรียน
บาคาร่า

5
หากข้อยกเว้นที่เราคาดว่าจะถูกโยนเป็นข้อยกเว้นที่ตรวจสอบแล้วเราควรเพิ่มการโยนหรือลองจับหรือทดสอบสถานการณ์นี้ในอีกทางหนึ่งหรือไม่?
Mohammad Jafar Mashhadi

5
@MartinTrummer ไม่ควรรันโค้ดหลังจาก foo.doStuff () เนื่องจากมีการโยนข้อยกเว้นและออกจากเมธอด การมีโค้ดหลังจากข้อยกเว้นที่คาดไว้ (ยกเว้นการปิดทรัพยากรในครั้งสุดท้าย) นั้นไม่สามารถช่วยเหลือได้เนื่องจากไม่ควรดำเนินการหากมีการโยนข้อยกเว้น
Jason Thompson

9
นี่เป็นวิธีที่ดีที่สุด มีข้อดีสองข้อที่นี่เมื่อเปรียบเทียบกับโซลูชันของ skaffman ประการแรกExpectedExceptionชั้นเรียนมีวิธีการจับคู่ข้อความของข้อยกเว้นหรือแม้กระทั่งการเขียน matcher ของคุณเองที่ขึ้นอยู่กับชั้นของข้อยกเว้น ประการที่สองคุณสามารถตั้งค่าความคาดหวังของคุณทันทีก่อนบรรทัดของรหัสที่คุณคาดว่าจะเกิดข้อยกเว้น - ซึ่งหมายความว่าการทดสอบของคุณจะล้มเหลวหากบรรทัดที่ไม่ถูกต้องของรหัสโยนข้อยกเว้น; ในขณะที่ไม่มีวิธีการทำเช่นนั้นด้วยวิธีแก้ปัญหาของ skaffman
Dawood ibn Kareem

5
@MJafarMash หากมีการตรวจสอบข้อยกเว้นที่คุณคาดว่าจะได้รับจากนั้นคุณจะเพิ่มข้อยกเว้นนั้นในข้อบททดสอบวิธีการทดสอบ คุณทำเช่นเดียวกันเมื่อใดก็ตามที่คุณกำลังทดสอบวิธีที่ประกาศว่าจะโยนข้อยกเว้นที่ตรวจสอบแม้ว่าจะไม่มีการเรียกใช้ข้อยกเว้นในกรณีทดสอบเฉพาะก็ตาม
NamshubWriter

472

ระมัดระวังการใช้ข้อยกเว้นที่คาดไว้เนื่องจากยืนยันว่าวิธีการโยนข้อยกเว้นนั้นไม่ใช่บรรทัดของรหัสเฉพาะในการทดสอบ

ฉันมักจะใช้สิ่งนี้สำหรับการตรวจสอบความถูกต้องของพารามิเตอร์เนื่องจากวิธีการดังกล่าวมักจะง่ายมาก แต่การทดสอบที่ซับซ้อนมากขึ้นอาจได้รับการบริการที่ดีกว่า

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

ใช้วิจารณญาณ


95
บางทีฉันเป็นโรงเรียนเก่า แต่ฉันก็ยังชอบสิ่งนี้อยู่ มันทำให้ฉันมีสถานที่สำหรับทดสอบข้อยกเว้น: บางครั้งฉันมีข้อยกเว้นด้วย getters สำหรับค่าบางอย่างหรือฉันอาจมองหาค่าเฉพาะในข้อความ (เช่นค้นหา "xyz" ในข้อความ "รหัสที่ไม่รู้จัก 'xyz' ")
Rodney Gitzel

3
ฉันคิดว่าวิธีการของ NamshubWriter นั้นมอบสิ่งที่ดีที่สุดสำหรับคุณทั้งสองโลก
Eddie

4
ใช้ ExpectedException คุณสามารถเรียก N exception.expect ต่อวิธีการทดสอบเช่น exception.expect นี้ (IndexOutOfBoundsException.class); foo.doStuff1 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff2 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff3 ();
user1154664

10
@ user1154664 ที่จริงแล้วคุณไม่สามารถ การใช้ ExpectedException คุณสามารถทดสอบได้ว่าวิธีหนึ่งมีข้อยกเว้นเนื่องจากเมื่อมีการเรียกใช้วิธีนั้นการทดสอบจะหยุดดำเนินการเพราะมันโยนข้อยกเว้นที่คาดไว้!
NamshubWriter

2
ประโยคแรกของคุณไม่จริง เมื่อใช้ExpectedExceptionงานสิ่งปกติที่ต้องทำคือการตั้งค่าความคาดหวังทันทีก่อนถึงบรรทัดที่คุณคาดว่าจะเกิดข้อยกเว้น ด้วยวิธีนี้หากบรรทัดก่อนหน้าเกิดข้อยกเว้นจะไม่ทำให้เกิดกฎและการทดสอบจะล้มเหลว
Dawood ibn Kareem

213

ตามที่ได้รับคำตอบก่อนหน้านี้มีหลายวิธีในการจัดการกับข้อยกเว้นใน JUnit แต่ด้วย Java 8 มีอีกหนึ่ง: การใช้แลมบ์ดานิพจน์ ด้วยแลมบ์ดานิพจน์เราสามารถบรรลุไวยากรณ์เช่นนี้

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

assertThrown ยอมรับอินเตอร์เฟสที่ใช้งานได้ซึ่งสามารถสร้างอินสแตนซ์ได้ด้วยการแสดงออกแลมบ์ดาการอ้างอิงเมธอดหรือการอ้างอิงคอนสตรัคเตอร์ assertThrown ยอมรับว่าอินเตอร์เฟสจะคาดหวังและพร้อมที่จะจัดการกับข้อยกเว้น

นี่เป็นเทคนิคที่ค่อนข้างง่าย แต่ทรงพลัง

ดูที่บล็อกโพสต์นี้อธิบายเทคนิคนี้: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

ซอร์สโค้ดสามารถพบได้ที่นี่: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

การเปิดเผยข้อมูล: ฉันเป็นผู้เขียนบล็อกและโครงการ


2
ฉันชอบโซลูชันนี้ แต่ฉันสามารถดาวน์โหลดได้จาก repo maven หรือไม่?
Selwyn

@Austuster หนึ่งการดำเนินการของความคิดนี้ที่มีอยู่ใน Maven คือstefanbirkner.github.io/vallado
NamshubWriter

6
@CristianoFontes API ที่ง่ายกว่านี้ถูกกำหนดไว้สำหรับ JUnit 4.13 ดูgithub.com/junit-team/junit/commit/…
NamshubWriter

@RafalBorowiec ทางเทคนิคnew DummyService()::someMethodเป็นMethodHandleแต่วิธีนี้ใช้ได้ดีกับแลมบ์ดานิพจน์
Andy

@ NamshubWriter ดูเหมือนว่า junit 4.13 ถูกทอดทิ้งเนื่องจาก junit 5: stackoverflow.com/questions/156503/…
Vadzim

154

ใน Junit มีสี่วิธีในการทดสอบข้อยกเว้น

junit5.x

  • สำหรับ junit5.x คุณสามารถใช้assertThrowsดังต่อไปนี้

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

junit4.x

  • สำหรับ junit4.x ให้ใช้แอตทริบิวต์ 'ที่คาดหวัง' ของบันทึกย่อแบบทดสอบ

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
  • สำหรับ junit4.x ให้ใช้กฎ 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();
        }
    }
  • คุณยังสามารถใช้วิธีลอง / จับแบบคลาสสิคที่ใช้กันอย่างแพร่หลายภายใต้กรอบ 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.
        }
    }
  • ดังนั้น

    • ถ้าคุณชอบ Junit 5 คุณควรจะชอบอันดับที่ 1
    • วิธีที่สองจะใช้เมื่อคุณต้องการทดสอบประเภทของข้อยกเว้นเท่านั้น
    • ทั้งสองอย่างแรกและสุดท้ายจะถูกใช้เมื่อคุณต้องการข้อความข้อยกเว้นการทดสอบเพิ่มเติม
    • ถ้าคุณใช้ junit 3 ก็จะเลือกอันที่ 4
  • สำหรับข้อมูลเพิ่มเติมคุณสามารถอ่านเอกสารนี้และคู่มือผู้ใช้ junit5เพื่อดูรายละเอียด


6
สำหรับฉันนี่คือคำตอบที่ดีที่สุดมันครอบคลุมทุกวิธีอย่างชัดเจนขอบคุณ! โดยส่วนตัวแล้วฉันใช้ตัวเลือกที่ 3 ต่อไปแม้จะใช้ Junit4 เพื่อให้อ่านได้เพื่อหลีกเลี่ยงการบล็อก catch ว่างคุณยังสามารถจับ e
Nicolas Cornette

เป็นไปได้หรือไม่ที่จะใช้ ExpectedException เพื่อคาดหวังข้อยกเว้นที่ตรวจสอบ
miuser

ทั้งหมดนี้คือการสะสมคำตอบสามอันดับแรก IMO คำตอบนี้ไม่ควรแม้แต่ถูกโพสต์หากยังไม่ได้เพิ่มอะไรใหม่ เพียงตอบ (คำถามยอดนิยม) สำหรับตัวแทน ค่อนข้างไร้ประโยชน์
Paul Samsotha

แน่ใจว่าเพราะคุณสามารถส่งผ่านวิธีใดที่ได้มาจากการวิธีการTrowable ExpectedException.expectโปรดดูมันลายเซ็น @miuser
walsh

116

TL; DR

  • post-JDK8: ใช้AssertJหรือ lambdas ที่กำหนดเองเพื่อยืนยันพฤติกรรมพิเศษ

  • ก่อน JDK8: ผมจะแนะนำดีเก่าtry- catchบล็อก ( อย่าลืมเพิ่มการfail()ยืนยันก่อนcatchบล็อก )

โดยไม่คำนึงถึง Junit 4 หรือ JUnit 5

เรื่องยาว

เป็นไปได้ที่จะเขียนตัวเองทำเอง try - catchบล็อกหรือใช้เครื่องมือ JUnit ( @Test(expected = ...)หรือ@Rule ExpectedExceptionคุณสมบัติกฎ JUnit)

แต่วิธีการเหล่านี้ไม่หรูหราและไม่สามารถอ่านได้ดีกับเครื่องมืออื่น ๆ ยิ่งไปกว่านั้นการใช้เครื่องมือ JUnit มีข้อผิดพลาดบางอย่าง

  1. try- catchบล็อกคุณต้องเขียนบล็อกรอบพฤติกรรมการทดสอบและเขียนยืนยันในการป้องกันการจับที่อาจจะดี แต่หลายคนพบว่าการขัดจังหวะรูปแบบนี้การไหลของการอ่านของการทดสอบ นอกจากนี้คุณต้องเขียนAssert.failที่ท้ายtryบล็อก มิฉะนั้นการทดสอบอาจพลาดด้านหนึ่งของการยืนยัน; PMD , findbugsหรือSonarจะพบปัญหาดังกล่าว

  2. @Test(expected = ...)คุณลักษณะเป็นที่น่าสนใจที่คุณสามารถเขียนโค้ดน้อยลงและแล้วเขียนการทดสอบนี้เป็นที่คาดคะเนน้อยแนวโน้มที่จะเกิดข้อผิดพลาดการเข้ารหัส แต่วิธีการนี้ยังขาดอยู่ในบางพื้นที่

    • หากการทดสอบจำเป็นต้องตรวจสอบสิ่งเพิ่มเติมเกี่ยวกับข้อยกเว้นเช่นสาเหตุหรือข้อความ (ข้อความข้อยกเว้นที่ดีมีความสำคัญจริง ๆ การมีประเภทข้อยกเว้นที่แม่นยำอาจไม่เพียงพอ)
    • เช่นเดียวกับความคาดหวังที่วางไว้ในวิธีการขึ้นอยู่กับวิธีการเขียนรหัสทดสอบแล้วส่วนที่ผิดของรหัสทดสอบสามารถโยนข้อยกเว้นนำไปสู่การทดสอบที่ผิดพลาดและฉันไม่แน่ใจว่าPMD , findbugsหรือSonarจะให้คำแนะนำเกี่ยวกับรหัสดังกล่าว

      @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. ExpectedExceptionกฎยังเป็นความพยายามที่จะแก้ไขคำเตือนก่อนหน้านี้ แต่มันให้ความรู้สึกอึดอัดเล็กน้อยกับการใช้งานที่จะใช้รูปแบบที่คาดหวังEasyMockผู้ใช้ทราบเป็นอย่างดีรูปแบบนี้ มันอาจจะสะดวกสำหรับบางคน แต่ถ้าคุณทำตามการพัฒนาพฤติกรรมขับเคลื่อน (BDD) หรือจัดเรียง Assert Act (AAA) หลักการExpectedExceptionกฎจะไม่พอดีกับสไตล์การเขียนเหล่านั้น นอกเหนือจากนั้นอาจประสบปัญหาเดียวกันกับ@Testทางขึ้นอยู่กับที่คุณวางความคาดหวัง

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

    แม้จะมีข้อยกเว้นที่คาดไว้ก่อนหน้าคำสั่งทดสอบ แต่จะทำให้การอ่านของคุณหยุดชะงักหากการทดสอบเป็นไปตาม BDD หรือ AAA

    นอกจากนี้ยังเห็นนี้ความคิดเห็นปัญหาใน JUnit ExpectedExceptionของผู้เขียน JUnit 4.13-beta-2ยังเลิกใช้กลไกนี้:

    ดึงคำขอ # 1519 : Deprecate ExpectedException

    เมธอด Assert.assertThrows จัดเตรียมวิธีที่ดีกว่าสำหรับการตรวจสอบข้อยกเว้น นอกจากนี้การใช้ ExpectedException นั้นเกิดข้อผิดพลาดได้ง่ายเมื่อใช้กับกฎอื่น ๆ เช่น TestWatcher เนื่องจากลำดับของกฎมีความสำคัญในกรณีนั้น

ดังนั้นตัวเลือกเหล่านี้มีข้อ จำกัด มากมายและไม่มีภูมิคุ้มกันต่อข้อผิดพลาดของ coder

  1. มีโครงการที่ผมเริ่มตระหนักถึงหลังจากการสร้างคำตอบนี้ว่ารูปลักษณ์ที่มีแนวโน้มของมันจับข้อยกเว้น

    ดังที่คำอธิบายของโครงการบอกไว้มันปล่อยให้ coder เขียนในโค้ดที่คล่องแคล่วจับข้อยกเว้นและเสนอข้อยกเว้นนี้สำหรับการยืนยันหลัง และคุณสามารถใช้ห้องสมุดการยืนยันใด ๆ เช่นHamcrestหรือAssertJ

    ตัวอย่างที่รวดเร็วนำมาจากหน้าแรก:

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

    ในขณะที่คุณสามารถเห็นรหัสตรงไปตรงมาคุณจับข้อยกเว้นในบรรทัดที่เฉพาะเจาะจงthenAPI เป็นนามแฝงที่จะใช้ AssertJ API (คล้ายกับการใช้assertThat(ex).hasNoCause()...) ในบางจุดที่โครงการที่พึ่ง FEST-ยืนยันบรรพบุรุษของ AssertJ แก้ไข:ดูเหมือนว่าโครงการกำลังรองรับ Java 8 Lambdas

    ปัจจุบันห้องสมุดนี้มีข้อบกพร่องสองประการ:

    • ในช่วงเวลาของการเขียนนี้เป็นที่น่าสังเกตว่าห้องสมุดแห่งนี้ตั้งอยู่บนพื้นฐานของ Mockito 1.x เนื่องจากมันสร้างจำลองของวัตถุที่ทดสอบหลังฉาก เนื่องจาก Mockito ยังไม่ได้อัปเดตไลบรารีนี้จึงไม่สามารถทำงานกับคลาสสุดท้ายหรือวิธีสุดท้ายได้ และถึงแม้ว่ามันจะอิงกับ Mockito 2 ในเวอร์ชันปัจจุบันสิ่งนี้จะต้องมีการประกาศผู้ทำเลียนแบบทั่วโลก ( inline-mock-maker) ซึ่งเป็นสิ่งที่อาจจะไม่ใช่สิ่งที่คุณต้องการ

    • มันยังต้องอาศัยการทดสอบอีกครั้ง

    ปัญหาเหล่านี้จะใช้ไม่ได้เมื่อห้องสมุดรองรับแลมบ์ดา อย่างไรก็ตามการทำงานจะซ้ำกันโดยชุดเครื่องมือ AssertJ

    การทั้งหมดเข้าบัญชีถ้าคุณไม่ต้องการที่จะใช้เครื่องมือจับข้อยกเว้นผมจะแนะนำวิธีที่ดีเก่าของtry- catchบล็อกอย่างน้อยขึ้นอยู่กับ JDK7 และสำหรับผู้ใช้ JDK 8 คุณอาจต้องการใช้ AssertJ เนื่องจากข้อเสนอนั้นอาจเป็นมากกว่าแค่การยกเว้นข้อยกเว้น

  2. ด้วย JDK8 lambdas เข้าสู่ฉากทดสอบและพวกเขาได้พิสูจน์แล้วว่าเป็นวิธีที่น่าสนใจในการยืนยันพฤติกรรมที่ยอดเยี่ยม AssertJ ได้รับการปรับปรุงเพื่อให้ API ที่คล่องแคล่วดีเพื่อยืนยันพฤติกรรมที่ยอดเยี่ยม

    และทดสอบตัวอย่างกับ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. ด้วยการเขียนใหม่ที่ใกล้เสร็จของ JUnit 5 การยืนยันได้รับการปรับปรุงเล็กน้อยพวกเขาอาจพิสูจน์ได้ว่าน่าสนใจเพราะออกนอกกรอบเพื่อยืนยันข้อยกเว้นอย่างเหมาะสม แต่จริงๆยืนยัน API assertThrowsก็ยังคงเป็นที่น่าสงสารบิตไม่มีอะไรนอก

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

    ตามที่คุณสังเกตเห็นว่าassertEqualsยังคงกลับมาvoidและเช่นนี้ไม่อนุญาตให้มีการยืนยันการผูกมัดเช่น AssertJ

    นอกจากนี้ถ้าคุณจำชื่อการปะทะกันด้วยMatcherหรือได้เตรียมที่จะตอบสนองการปะทะกันเช่นเดียวกันกับAssertAssertions

ฉันต้องการสรุปว่าวันนี้ (2017-03-03) AssertJใช้งานง่าย, API ที่ค้นพบได้, การพัฒนาอย่างรวดเร็วและการพึ่งพาการทดสอบจริงเป็นทางออกที่ดีที่สุดสำหรับ JDK8 โดยไม่คำนึงถึงกรอบการทดสอบ (JUnit หรือไม่) JDK ก่อนหน้านั้นควรพึ่งพาtry-catchบล็อกแม้ว่าพวกเขาจะรู้สึกว่าเป็น clunky

คำตอบนี้ถูกคัดลอกมาจากคำถามอื่นที่ไม่มีทัศนวิสัยเหมือนกันฉันเป็นผู้เขียนคนเดียวกัน


1
การเพิ่ม org.junit.jupiter: junit-jupiter-engine: 5.0.0-RC2 การพึ่งพา (นอกเหนือจาก Junit ที่มีอยู่แล้ว: junit: 4.12) เพื่อให้สามารถใช้ assertThrows อาจไม่ใช่วิธีที่ต้องการ แต่ไม่ได้ทำให้เกิดปัญหาใด ๆ ปัญหาสำหรับฉัน
2560

ฉันเป็นแฟนตัวยงของการใช้กฎ ExpectedException แต่มันทำให้ฉันรำคาญเสมอว่ามันหยุดด้วย AAA คุณได้เขียนบทความที่ยอดเยี่ยมเพื่ออธิบายวิธีการที่แตกต่างกันทั้งหมดและคุณสนับสนุนให้ฉันลอง AssertJ :-) ขอบคุณ!
Pim Hazebroek

@PimHazebroek ขอบคุณ AssertJ API ค่อนข้างสมบูรณ์ ดีกว่าในความเห็นของฉันว่าสิ่งที่ JUnit เสนอออกมาจากกล่อง
Brice

64

ตอนนี้ปล่อย JUnit 5 และ JUnit 4.13 ตัวเลือกที่ดีที่สุดคือการใช้Assertions.assertThrows() (สำหรับ JUnit 5) และAssert.assertThrows()(สำหรับ JUnit 4.13) ดูJunit 5 คู่มือการใช้งาน

นี่คือตัวอย่างที่ตรวจสอบข้อยกเว้นถูกโยนทิ้งและใช้ความจริงเพื่อยืนยันในข้อความข้อยกเว้น:

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

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

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

ข้อดีกว่าวิธีการในคำตอบอื่น ๆ คือ:

  1. สร้างขึ้นใน JUnit
  2. คุณได้รับข้อความแสดงข้อยกเว้นที่มีประโยชน์หากโค้ดในแลมบ์ดาไม่ส่งข้อยกเว้นและสแต็คติดตามถ้ามันโยนข้อยกเว้นอื่น
  3. กระชับ
  4. ช่วยให้การทดสอบของคุณเป็นไปตาม Arrange-Act-Assert
  5. คุณสามารถระบุได้อย่างแม่นยำว่ารหัสใดที่คุณคาดว่าจะเกิดข้อยกเว้น
  6. คุณไม่จำเป็นต้องแสดงรายการข้อยกเว้นที่คาดไว้ในthrowsข้อ
  7. คุณสามารถใช้กรอบการยืนยันที่คุณเลือกเพื่อยืนยันเกี่ยวกับข้อยกเว้นที่ตรวจพบ

วิธีการที่คล้ายกันจะถูกเพิ่มorg.junit Assertใน JUnit 4.13


วิธีนี้สะอาด แต่ฉันไม่เห็นว่าวิธีนี้ช่วยให้การทดสอบของเราเป็นไปตาม "Arrange-Act-Assert" เนื่องจากเราต้องห่อส่วน "พระราชบัญญัติ" ใน "assertThrow" ซึ่งเป็นข้อยืนยัน
ลาน

@Clockwork แลมบ์ดาเป็น "การกระทำ" เป้าหมายของ Arrange-Act-Assert คือการทำให้โค้ดสะอาดและเรียบง่าย (และง่ายต่อการเข้าใจและบำรุงรักษา) ตามที่คุณระบุไว้วิธีการนี้สะอาด
NamshubWriter

ฉันยังคงหวังว่าฉันสามารถยืนยันการโยนและข้อยกเว้นในตอนท้ายของการทดสอบแม้ว่าในส่วน "ยืนยัน" ในวิธีการนี้คุณต้องปิดการกระทำในการยืนยันก่อนที่จะจับมันก่อน
ลาน

นั่นจะต้องใช้รหัสมากขึ้นในการทดสอบทุกครั้งเพื่อยืนยัน นั่นเป็นรหัสที่มากกว่าและจะเกิดข้อผิดพลาดได้ง่าย
NamshubWriter

43

วิธีการเกี่ยวกับสิ่งนี้: ตรวจสอบข้อยกเว้นทั่วไปตรวจสอบให้แน่ใจว่ามันทำออกมาจากบล็อก catch แล้วยืนยันว่าคลาสของข้อยกเว้นเป็นสิ่งที่คุณคาดหวัง การยืนยันนี้จะล้มเหลวหากก) ข้อยกเว้นนั้นเป็นประเภทที่ไม่ถูกต้อง (เช่นหากคุณมี Null Pointer แทน) และ b) ข้อยกเว้นไม่ได้ถูกโยนทิ้งไป

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}

3
นอกจากนี้คุณจะไม่เห็นว่าข้อยกเว้นแบบใดที่อยู่ในผลการทดสอบเมื่อวันนั้นมาถึงจุดที่การทดสอบล้มเหลว
jontejj

สิ่งนี้สามารถปรับปรุงได้เล็กน้อยโดยการเปลี่ยนวิธีการยืนยันในตอนท้าย assertEquals(ExpectedException.class, e.getClass())จะแสดงให้คุณเห็นค่าที่คาดหวังและเป็นจริงเมื่อการทดสอบล้มเหลว
Cypher

37

โซลูชันสไตล์BDD : JUnit 4 + ข้อยกเว้น Catch + AssertJ

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

36

การใช้AssertJ การยืนยันซึ่งสามารถใช้ร่วมกับ JUnit:

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)เพราะรับประกันได้ว่าบรรทัดที่คาดหวังในการทดสอบจะโยนข้อยกเว้นและช่วยให้คุณตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับข้อยกเว้นเช่นข้อความได้ง่ายขึ้น:

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

คำแนะนำ Maven / Gradle ที่นี่


วิธีที่รัดกุมที่สุดและไม่มีใครชื่นชมมันแปลก .. ฉันมีปัญหาเดียวกับห้องสมุด assertJ ยืนยันว่าชื่อขัดแย้งกับปัญญาของ Junit เพิ่มเติมเกี่ยวกับ assertJ Throwby: JUnit: การทดสอบข้อยกเว้นด้วย Java 8 และ AssertJ 3.0.0 ~ Codeleak.pl
ycomp

@ycomp มันเป็นคำตอบใหม่สำหรับคำถามที่เก่ามากดังนั้นความแตกต่างของคะแนนจึงเป็นการหลอกลวง
weston

นั่นอาจเป็นทางออกที่ดีที่สุดถ้าใครสามารถใช้ Java 8 และ AssertJ!
Pierre Henry

@ycomp ฉันสงสัยว่าความขัดแย้งของชื่อนี้อาจเกิดจากการออกแบบ: ไลบรารี AssertJ จึงขอแนะนำให้คุณไม่ใช้ JUnit assertThatเป็น AssertJ เสมอ นอกจากนี้เมธอด JUnit จะส่งกลับเฉพาะชนิด "ปกติ" ในขณะที่เมธอด AssertJ ส่งคืนAbstractAssertคลาสย่อย ... อนุญาตให้ใช้วิธีการแบบสตริงด้านบน (หรือเงื่อนไขทางเทคนิคสำหรับสิ่งนี้ ... )
ไมค์หนู

@weston จริง ๆ แล้วฉันเพิ่งใช้เทคนิคของคุณใน AssertJ 2.0.0 ไม่มีข้อแก้ตัวสำหรับการไม่อัพเกรดไม่ต้องสงสัยเลย แต่คุณอาจต้องการที่จะรู้
ไมค์หนู

33

เพื่อแก้ปัญหาเดียวกันฉันได้ตั้งโครงการขนาดเล็ก: http://code.google.com/p/catch-exception/

ใช้ผู้ช่วยตัวน้อยนี้คุณจะเขียน

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

นี่คือ verbose น้อยกว่ากฎ ExpectedException ของ JUnit 4.7 เมื่อเปรียบเทียบกับโซลูชันที่จัดทำโดย skaffman คุณสามารถระบุบรรทัดของโค้ดที่คุณคาดว่าจะได้รับการยกเว้น ฉันหวังว่านี่จะช่วยได้.


ฉันคิดเกี่ยวกับการทำสิ่งนี้เช่นกัน แต่ในที่สุดค้นพบว่าพลังที่แท้จริงของ ExpectedException ไม่เพียง แต่คุณสามารถระบุข้อยกเว้นที่คาดไว้ แต่คุณยังสามารถระบุคุณสมบัติบางอย่างของข้อยกเว้นเช่นสาเหตุที่คาดหวังหรือข้อความที่คาดหวัง
Jason Thompson

ฉันเดาว่าโซลูชันนี้มีข้อเสียเหมือนกับ mocks บ้างไหม? ตัวอย่างเช่นถ้าfooเป็นfinalก็จะล้มเหลวเพราะคุณไม่สามารถพร็อกซี่foo?
Tom

Tom ถ้า doStuff () เป็นส่วนหนึ่งของอินเตอร์เฟส มิฉะนั้นวิธีการนี้จะล้มเหลวคุณพูดถูก
rwitzel

31

อัปเดต: JUnit5 มีการปรับปรุงสำหรับการทดสอบข้อยกเว้น:assertThrowsมีการปรับปรุงสำหรับข้อยกเว้นการทดสอบ:

ตัวอย่างต่อไปนี้มาจาก: คู่มือผู้ใช้ Junit 5

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

คำตอบเดิมโดยใช้ JUnit 4

มีหลายวิธีในการทดสอบว่ามีการโยนข้อยกเว้น ฉันยังได้กล่าวถึงตัวเลือกด้านล่างในโพสต์ของฉันวิธีการเขียนการทดสอบหน่วยที่ยอดเยี่ยมกับ JUnit

ตั้งค่าพารามิเตอร์expected@Test(expected = FileNotFoundException.class)

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

การใช้ 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!"));
    }

}

ทดสอบกับ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");
}

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับข้อยกเว้นการทดสอบในJUnit4 วิกิพีเดียสำหรับการทดสอบข้อยกเว้นและbad.robot - คาดว่าข้อยกเว้น JUnit กฎ


22

คุณยังสามารถทำสิ่งนี้:

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

12
ในการทดสอบ JUnit จะเป็นการดีกว่าที่จะใช้Assert.fail()ไม่ใช่assertในกรณีที่การทดสอบของคุณทำงานในสภาพแวดล้อมที่ไม่ได้เปิดใช้งานการยืนยัน
NamshubWriter

14

IMHO วิธีที่ดีที่สุดในการตรวจสอบข้อยกเว้นใน JUnit คือรูปแบบ try / catch / fail / assert:

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

assertTrueอาจจะมีบิตที่แข็งแกร่งสำหรับบางคนจึงassertThat(e.getMessage(), containsString("the message");อาจจะดีกว่า


13

โซลูชัน JUnit 5

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

ข้อมูลเพิ่มเติมเกี่ยวกับ JUnit 5 ในhttp://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions


expectThrows()เป็นส่วนหนึ่งของ TestNG ไม่ใช่ JUnit
Lu55

13

คำตอบที่มีความยืดหยุ่นและสง่างามมากที่สุดสำหรับ Junit 4 ผมพบว่าในบล็อก Mkyong มันมีความยืดหยุ่นในการtry/catchใช้@Ruleคำอธิบายประกอบ ฉันชอบวิธีนี้เพราะคุณสามารถอ่านคุณสมบัติเฉพาะของข้อยกเว้นที่กำหนดเองได้

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

ฉันลองวิธีการมากมายที่นี่ แต่มันซับซ้อนหรือไม่ตรงกับความต้องการของฉัน ในความเป็นจริงเราสามารถเขียนวิธีช่วยได้ง่ายๆ

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

ใช้แบบนี้:

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

Zero dependencies: ไม่จำเป็นต้องมี mockito, ไม่จำเป็นต้องมี powermock; และใช้ได้ดีกับคลาสสุดท้าย


ที่น่าสนใจ แต่ไม่เหมาะกับ AAA (Arrange Act Assert) ซึ่งคุณต้องการทำ Act และ Assert ในขั้นตอนที่แตกต่างกันจริง ๆ
bln-tom

1
@ bln-tom ในทางเทคนิคมันเป็นสองขั้นตอนที่แตกต่างกันพวกเขาไม่ได้อยู่ในลำดับที่ ; p
Trejkaz

10

โซลูชัน Java 8

หากคุณต้องการโซลูชันที่:

  • ใช้ประโยชน์จาก lambdas Java 8
  • ไม่ได้ขึ้นอยู่กับเวทมนตร์ของ JUnit
  • ช่วยให้คุณสามารถตรวจสอบข้อยกเว้นหลายอย่างภายในวิธีทดสอบเดียว
  • ตรวจสอบข้อยกเว้นที่ถูกส่งออกมาโดยชุดบรรทัดเฉพาะในวิธีการทดสอบของคุณแทนที่จะเป็นบรรทัดที่ไม่รู้จักในวิธีการทดสอบทั้งหมด
  • ส่งมอบออบเจ็กต์ข้อยกเว้นตามจริงที่เกิดขึ้นเพื่อให้คุณสามารถตรวจสอบเพิ่มเติมได้

นี่คือฟังก์ชั่นยูทิลิตี้ที่ฉันเขียน:

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

( นำมาจากบล็อกของฉัน )

ใช้มันดังต่อไปนี้:

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


8

ในกรณีของฉันฉันได้รับ RuntimeException จาก db เสมอ แต่ข้อความต่างกัน และต้องจัดการข้อยกเว้นตามลำดับ นี่คือวิธีที่ฉันทดสอบ:

@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
ก่อนที่จะถึงตอน} catch (นี้คุณควรจะแทรกfail("no exception thrown");
Daniel Alder

6

เพียงสร้าง Matcher ที่สามารถปิดและเปิดได้เช่นนี้

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

วิธีใช้:

เพิ่มpublic ExpectedException exception = ExpectedException.none();จากนั้น:

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

6

ใน JUnit 4 หรือใหม่กว่าคุณสามารถทดสอบข้อยกเว้นดังนี้

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


สิ่งนี้มีคุณสมบัติมากมายที่สามารถใช้ในการปรับปรุงการทดสอบ JUnit ของเรา
หากคุณเห็นตัวอย่างด้านล่างฉันกำลังทดสอบ 3 สิ่งในข้อยกเว้น

  1. ประเภทของข้อยกเว้นโยน
  2. ข้อความแสดงข้อยกเว้น
  3. สาเหตุของข้อยกเว้น


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

เราสามารถใช้การยืนยันที่ล้มเหลวหลังจากวิธีที่ต้องส่งคืนข้อยกเว้น:

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
ที่สองcatchจะกลืนร่องรอยสแต็กถ้ามีข้อยกเว้นอื่น ๆ ถูกโยนสูญเสียข้อมูลที่เป็นประโยชน์
NamshubWriter

5

นอกจากนี้สิ่งที่NamShubWriterพูดได้ตรวจสอบให้แน่ใจว่า:

  • อินสแตนซ์ ExpectedException เป็นแบบสาธารณะ ( คำถามที่เกี่ยวข้อง )
  • ExpectedException ไม่ได้สร้างอินสแตนซ์ในคำพูด, วิธีการ @Before โพสต์นี้จะอธิบายความซับซ้อนทั้งหมดของคำสั่งการดำเนินการของ JUnit อย่างชัดเจน

อย่าได้ทำเช่นนี้:

@Rule    
public ExpectedException expectedException;

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

ในที่สุดโพสต์บล็อกนี้แสดงให้เห็นอย่างชัดเจนถึงวิธีการยืนยันว่ามีการโยนข้อยกเว้นบางอย่าง


4

ฉันขอแนะนำห้องสมุดassertj-coreเพื่อจัดการข้อยกเว้นในการทดสอบ Junit

ใน java 8 เช่นนี้

//given

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

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

2

วิธีการแก้ปัญหา Junit4 กับ Java8 คือการใช้ฟังก์ชั่นนี้:

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

การใช้งานคือ:

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

โปรดทราบว่าข้อ จำกัด เพียงอย่างเดียวคือการใช้การfinalอ้างอิงวัตถุในการแสดงออกแลมบ์ดา โซลูชันนี้อนุญาตให้ทำการทดสอบการยืนยันต่อไปแทนที่จะคาดหวังว่าจะได้ระดับที่วิธีการใช้@Test(expected = IndexOutOfBoundsException.class)โซลูชัน


1

ยกตัวอย่างเช่นคุณต้องการเขียน Junit สำหรับส่วนย่อยของรหัสที่ระบุไว้ด้านล่าง

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

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

รหัสข้างต้นคือการทดสอบสำหรับข้อยกเว้นที่ไม่รู้จักที่อาจเกิดขึ้นและด้านล่างหนึ่งคือการยืนยันข้อยกเว้นบางอย่างกับข้อความที่กำหนดเอง

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

               }

    }

นี่เป็นอีกวิธีในการตรวจสอบวิธีการโยนข้อยกเว้นที่ถูกต้องหรือไม่


1

กรอบ JUnit มีassertThrows()วิธีการ:

ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
    calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
  • สำหรับ JUnit 5 มันอยู่ในorg.junit.jupiter.api.Assertionsชั้นเรียน;
  • สำหรับ JUnit 4.13 มันอยู่ในorg.junit.Assertชั้นเรียน
  • สำหรับ JUnit 4 เวอร์ชันก่อนหน้า: เพียงเพิ่มการอ้างอิงในorg.junit.jupiter:junit-jupiter-apiโครงการของคุณและคุณจะได้รับเวอร์ชันการทำงานที่สมบูรณ์แบบจาก JUnit 5

0

ด้วย Java 8 คุณสามารถสร้างวิธีการใช้รหัสเพื่อตรวจสอบและยกเว้นเป็นพารามิเตอร์:

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

แล้วในการทดสอบของคุณ:

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

ประโยชน์ที่ได้รับ:

  • ไม่ต้องพึ่งพาห้องสมุดใด ๆ
  • การตรวจสอบที่มีการแปล - แม่นยำยิ่งขึ้นและอนุญาตให้มีการยืนยันหลายอย่างเช่นนี้ภายในการทดสอบหนึ่งครั้งหากจำเป็น
  • ง่ายต่อการใช้
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.