ฉันจะยืนยันว่า Iterable มีองค์ประกอบที่มีคุณสมบัติบางอย่างได้อย่างไร


104

สมมติว่าฉันต้องการทดสอบหน่วยวิธีด้วยลายเซ็นนี้:

List<MyItem> getMyItems();

สมมติว่าMyItemเป็น Pojo ที่มีคุณสมบัติหลายอย่างซึ่งหนึ่งในนั้น"name"เข้าถึงได้ผ่านทางgetName().

สิ่งที่ฉันสนใจเกี่ยวกับการตรวจสอบก็คือList<MyItem>หรือใด ๆ ที่IterableมีสองMyItemอินสแตนซ์ซึ่ง"name"คุณสมบัติมีค่า"foo"และ"bar". หากคุณสมบัติอื่นไม่ตรงกันฉันไม่สนใจวัตถุประสงค์ของการทดสอบนี้จริงๆ หากชื่อตรงกันแสดงว่าสำเร็จแล้ว

ฉันต้องการให้เป็นซับเดียวถ้าเป็นไปได้ นี่คือ "ไวยากรณ์หลอก" บางส่วนที่ฉันอยากจะทำ

assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});

Hamcrest จะดีสำหรับสิ่งประเภทนี้หรือไม่? ถ้าเป็นเช่นนั้นไวยากรณ์หลอกของฉันจะเป็นอย่างไร

คำตอบ:


125

ขอบคุณ @Razvan ที่ชี้ทางที่ถูกต้องให้ฉัน ฉันสามารถรับมันได้ในหนึ่งบรรทัดและฉันตามล่าการนำเข้าสำหรับ Hamcrest 1.3 ได้สำเร็จ

การนำเข้า:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;

รหัส:

assertThat( myClass.getMyItems(), contains(
    hasProperty("name", is("foo")), 
    hasProperty("name", is("bar"))
));

49

ลอง:

assertThat(myClass.getMyItems(),
                          hasItem(hasProperty("YourProperty", is("YourValue"))));

2
เช่นเดียวกับโหนดด้านข้าง - นี่คือวิธีแก้ปัญหาอุปสรรค (ไม่ใช่การยืนยัน)
Hartmut P.

47

ไม่ใช่ Hamcrest โดยเฉพาะ แต่ฉันคิดว่ามันคุ้มค่าที่จะพูดถึงที่นี่ สิ่งที่ฉันใช้บ่อยใน Java8 คือ:

assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));

(แก้ไขเพื่อการปรับปรุงเล็กน้อยของ Rodrigo Manyari มันดูละเอียดกว่าเล็กน้อยดูความคิดเห็น)

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


2
การปรับปรุงเล็กน้อย: assertTrue (myClass.getMyItems (). stream (). anyMatch (item -> "foo" .equals (item.getName ()));
Rodrigo Manyari

@RodrigoManyari ปิดวงเล็บหายไป
Abdull

1
โซลูชันนี้ทำให้เสียความเป็นไปได้ที่จะแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสม
Giulio Caccin

@GiulioCaccin ฉันไม่คิดว่ามันจะเป็นเช่นนั้น หากคุณใช้ JUnit คุณสามารถ / ควรใช้วิธีการยืนยันที่มากเกินไปและเขียน assertTrue (... , "ข้อความทดสอบความล้มเหลวของฉันเอง"); ดูเพิ่มเติมได้ที่junit.org/junit5/docs/current/api/org/junit/jupiter/api/…
Mario Eis

ฉันหมายความว่าถ้าคุณยืนยันกับบูลีนคุณจะสูญเสียความสามารถในการพิมพ์ความแตกต่างที่เกิดขึ้นจริง / ที่คาดไว้โดยอัตโนมัติ เป็นไปได้ที่จะยืนยันโดยใช้ตัวจับคู่ แต่คุณต้องแก้ไขคำตอบนี้ให้คล้ายกับคำตอบอื่น ๆ ในหน้านี้จึงจะทำได้
Giulio Caccin

20

Assertjเก่งเรื่องนี้

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

    assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");

ข้อดีที่ยิ่งใหญ่สำหรับการยืนยันเมื่อเทียบกับแฮมเครสต์คือการใช้โค้ดที่สมบูรณ์


16

AssertJ มีคุณสมบัติที่ยอดเยี่ยมในextracting(): คุณสามารถส่งFunctions เพื่อแยกฟิลด์ ให้ตรวจสอบในเวลารวบรวม
คุณสามารถยืนยันขนาดก่อนได้อย่างง่ายดาย

มันจะให้:

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

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName)
          .containsExactlyInAnyOrder("foo", "bar"); 

containsExactlyInAnyOrder() ยืนยันว่ารายการมีเฉพาะค่าเหล่านี้ไม่ว่าจะเป็นลำดับใดก็ตาม

เพื่อยืนยันว่ารายการมีค่าเหล่านี้ไม่ว่าจะเป็นลำดับใดก็ตาม แต่อาจมีค่าอื่น ๆ ให้ใช้contains():

.contains("foo", "bar"); 

หมายเหตุด้านข้าง: ในการยืนยันหลายฟิลด์จากองค์ประกอบของ a Listด้วย AssertJ เราทำได้โดยการรวมค่าที่คาดหวังสำหรับแต่ละองค์ประกอบลงในtuple()ฟังก์ชัน:

import static org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName, MyItem::getOtherValue)
          .containsExactlyInAnyOrder(
               tuple("foo", "OtherValueFoo"),
               tuple("bar", "OtherValueBar")
           ); 

4
อย่าเข้าใจว่าทำไมถึงไม่มีการโหวตเพิ่ม ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดแล้ว
PeMa

1
ไลบรารี assertJ นั้นอ่านได้ง่ายกว่ามากจากนั้น JUnit assertion API
Sangimed

@Sangimed เห็นด้วยและฉันชอบที่จะแฮมเครสต์
davidxxx

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

5

ตราบเท่าที่รายการของคุณเป็นคลาสที่เป็นรูปธรรมคุณสามารถเรียกใช้วิธีการ contains () ได้ตราบเท่าที่คุณใช้วิธีการเท่ากับ () ใน MyItem

// given 
// some input ... you to complete

// when
List<MyItems> results = service.getMyItems();

// then
assertTrue(results.contains(new MyItem("foo")));
assertTrue(results.contains(new MyItem("bar")));

ถือว่าคุณได้ติดตั้งตัวสร้างที่ยอมรับค่าที่คุณต้องการยืนยัน ฉันตระหนักดีว่าสิ่งนี้ไม่ได้อยู่ในบรรทัดเดียว แต่มีประโยชน์ที่จะทราบว่าค่าใดหายไปแทนที่จะตรวจสอบทั้งสองอย่างพร้อมกัน


1
ฉันชอบวิธีแก้ปัญหาของคุณมาก แต่เขาควรดัดแปลงโค้ดทั้งหมดเพื่อทดสอบหรือไม่
Kevin Bowersox

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

ควรรวมข้อความไว้ใน assertTrue ด้วยเพื่อให้ข้อความแสดงข้อผิดพลาดเข้าใจได้ง่ายขึ้น หากไม่มีข้อความหากล้มเหลว JUnit จะโยน AssertionFailedError โดยไม่มีข้อความแสดงข้อผิดพลาดใด ๆ ดังนั้นควรใส่บางสิ่งเช่น "ผลลัพธ์ควรมี MyItem ใหม่ (\" foo \ ")"
สูงสุด

ใช่คุณถูก. ฉันขอแนะนำ Hamcrest ไม่ว่าในกรณีใด ๆ และฉันไม่เคยใช้ assertTrue () วันนี้
Brad

โปรดทราบว่า POJO หรือ DTO ของคุณควรกำหนดวิธีการเท่ากับ
Tayab Hussain

1

AssertJ 3.9.1 สนับสนุนการใช้เพรดิเคตโดยตรงในanyMatchวิธีการ

assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())

โดยทั่วไปแล้วเป็นกรณีการใช้งานที่เหมาะสมสำหรับเงื่อนไขที่ซับซ้อนโดยพลการ

สำหรับเงื่อนไขง่ายๆฉันชอบใช้extractingวิธีการ (ดูด้านบน) เนื่องจากผลลัพธ์ที่สามารถทำซ้ำได้ภายใต้การทดสอบอาจรองรับการตรวจสอบค่าที่อ่านได้ง่ายขึ้น ตัวอย่าง: สามารถให้ API พิเศษเช่นcontainsmethod ในคำตอบของ Frank Neblung หรือคุณสามารถโทรหาได้anyMatchในภายหลังและใช้การอ้างอิงวิธีการเช่น"searchedvalue"::equals. นอกจากนี้ยังสกัดหลาย ๆ สามารถใส่ลงไปในวิธีการที่ผลการตรวจสอบต่อมาใช้extractingtuple()

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