ยืนยันเท่ากับระหว่าง 2 รายการใน Junit


165

ฉันจะยืนยันความเท่าเทียมกันระหว่างรายการในกรณีทดสอบJUnit ได้อย่างไร ความเท่าเทียมกันควรอยู่ระหว่างเนื้อหาของรายการ

ตัวอย่างเช่น:

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four"); 

// numbers should be equal to numbers2
//numbers should not be equal to numbers3

5
ฉันชอบที่จะใช้assertArrayEqualsในปัจจุบัน List#toArrayการใช้งานร่วมกับ
Thibstars

@Thibstars - ฉัน upvote ว่าเป็นคำตอบ
dfrankow

2
assertArrayEqualsคุณต้องได้รับอาร์เรย์จากรายการของคุณ คุณสามารถดำเนินการโดยตรงในรายการโดยใช้assertIterableEquals
ThetaSinner

assertIterableEqualsพร้อมใช้งานสำหรับ jUnit5 @ThetaSinner
iec2011007

คำตอบ:


170

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

@Test
public void test_array_pass()
{
  List<String> actual = Arrays.asList("fee", "fi", "foe");
  List<String> expected = Arrays.asList("fee", "fi", "foe");

  assertThat(actual, is(expected));
  assertThat(actual, is(not(expected)));
}

หากคุณมี Junit รุ่นล่าสุดติดตั้งด้วย hamcrest เพียงเพิ่มการนำเข้าเหล่านี้:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)

http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html

http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html


3
System.out.println(actual == expected);จะกลับเท็จ แต่System.out.println(actual.equals(expected));จะกลับจริง
ปลาดุก

@ แคทฟิชใช่มันสับสนใช่มั้ย ฉันคิดว่าฉันแสดงให้เห็นว่าผู้จับคู่ใช้.equals(..)แทน==หรือไม่
djeikyb

2
มันดีกว่า assertEquals อย่างไร
Raedwald

1
@Raedwald ผลลัพธ์เมื่อการยืนยันล้มเหลว ฉันจะพยายามกลับมาใหม่ในภายหลังเพื่อแก้ไขความแตกต่าง Hamcrest matchers สามารถสร้างข้อความล้มเหลวอย่างละเอียด เป็นไปได้สำหรับ junit ที่จะเพียงแค่ยืนยัน assertEquals ด้วยความดีที่คล้ายกัน แต่ส่วนใหญ่ Junit มีคุณสมบัติการทดสอบหน่วยหลักและ hamcrest ให้ไลบรารี describer แตกต่างของวัตถุที่ดี
djeikyb

29

อย่าแปลงเป็นสตริงและเปรียบเทียบ สิ่งนี้ไม่ดีสำหรับความสมบูรณ์ ใน Junit ภายใน Corematchers มีผู้จับคู่สำหรับสิ่งนี้ =>hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

นี่เป็นวิธีที่ดีกว่าที่ฉันรู้ในการตรวจสอบองค์ประกอบในรายการ


2
ควรเป็นคำตอบที่เลือกพร้อมหมายเหตุเดียว: คุณต้องตรวจสอบด้วยว่าไม่มีรายการอื่นในรายการนอกเหนือจากที่คุณต้องการ อาจใช้:Assert.assertEquals(4,yourList.size());
yoni

หรือด้วยบรรทัดเดียว:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
1778602

3
"สิ่งนี้ไม่ดีสำหรับความสมบูรณ์แบบ" →ฉันไม่แน่ใจว่าระดับใดที่ควรคำนึงถึงประสิทธิภาพเมื่อเขียนการทดสอบหน่วย ... แต่แน่นอนว่าการเปรียบเทียบสตริงผ่านtoString()เวอร์ชันไม่ใช่วิธีที่ดีที่สุด
walen

21

นี่เป็นคำตอบเดิมซึ่งเหมาะสำหรับ JUnit 4.3 และต่ำกว่า JUnit เวอร์ชันทันสมัยมีข้อความล้มเหลวที่สามารถอ่านได้ในตัวในเมธอด assertThat ต้องการคำตอบอื่น ๆ สำหรับคำถามนี้ถ้าเป็นไปได้

List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
            "\n  'a'        = "+a+
            "\n  'expected' = "+expected, 
            expected.equals(a));

สำหรับเร็กคอร์ดตามที่ @Paul กล่าวถึงในความคิดเห็นของเขาต่อคำตอบนี้สองLists จะเท่ากัน:

ถ้าและถ้าวัตถุที่ระบุเป็นรายการทั้งสองรายการจะมีขนาดเท่ากันและคู่ขององค์ประกอบที่สอดคล้องกันทั้งหมดในรายการทั้งสองจะเท่ากัน (สององค์ประกอบe1และe2เท่ากับถ้า(e1==null ? e2==null : e1.equals(e2)).) ในคำอื่น ๆ สองรายการจะถูกกำหนดให้เท่ากับหากพวกเขามีองค์ประกอบเดียวกันในลำดับเดียวกัน คำจำกัดความนี้ทำให้มั่นใจได้ว่าวิธีการที่เท่าเทียมกันทำงานได้อย่างถูกต้องในการใช้งานที่แตกต่างกันของListอินเตอร์เฟซ

ดูJavaDocs ของListอินเตอร์เฟซ


1
ดังนั้นคุณหมายถึงที่คาดไว้เท่ากับ (a) จะจัดการกับวัตถุที่รายการนั้นกำลังถือครองอยู่หรือไม่
Kamal

1
จากรายการ javadoc: เปรียบเทียบวัตถุที่ระบุกับรายการนี้เพื่อความเท่าเทียมกัน ส่งคืนค่าจริงถ้าและถ้าวัตถุที่ระบุเป็นรายการทั้งสองรายการจะมีขนาดเท่ากันและคู่ขององค์ประกอบที่เกี่ยวข้องในรายการทั้งสองนั้นเท่ากัน (สององค์ประกอบ e1 และ e2 มีค่าเท่ากันถ้า (e1 == null? e2 == ว่าง: e1.equals (e2))) ในคำอื่น ๆ รายการสองรายการจะถูกกำหนดให้เท่ากันหากมีองค์ประกอบเดียวกันในลำดับเดียวกัน . คำจำกัดความนี้ทำให้มั่นใจได้ว่าวิธีการที่เท่าเทียมกันทำงานได้อย่างถูกต้องในการใช้งานที่แตกต่างกันของรายการอินเตอร์เฟซ
Paul McKenzie

1
อนิจจานี้ให้ข้อผิดพลาดที่เป็นประโยชน์น้อยกว่า ฉันพบว่ามันดีกว่าที่จะเขียนคลาสยูทิลิตี้ที่มีการวนรอบเพื่อให้คุณสามารถดูองค์ประกอบที่แตกต่างกัน
Michael Lloyd Lee mlk

@mlk อาจ แต่ฉันลังเลที่จะเขียนวิธีการอรรถประโยชน์ที่กำหนดเองสำหรับสิ่งนั้น ตอนนี้ฉันได้แก้ไขข้อความแสดงข้อผิดพลาดแล้ว
Bart Kiers

@mlk ฉันยอมรับว่ามันอาจจะดีกว่าที่จะเขียนลูปเพื่อทดสอบแต่ละองค์ประกอบเพื่อให้คุณรู้ว่าอะไรแตกต่างกันไป ขึ้นอยู่กับประเภทของวัตถุที่อยู่ในรายการ หากพวกเขาเป็น Strings เพียงแค่พูดว่า "a =" + a ควรจะดี แต่ถ้าพวกเขาเป็นวัตถุที่ซับซ้อน (รายการอื่น ๆ หรือสิ่งที่ไม่มีการดำเนินการที่ดีในการรวบรวม) มันอาจจะง่ายกว่าที่จะทดสอบพวกเขาเป็นรายบุคคล
Matt N

20

assertEquals(Object, Object)จาก JUnit4 / JUnit 5 หรือ assertThat(actual, is(expected));จาก Hamcrest ที่เสนอในคำตอบอื่น ๆ จะใช้งานได้ทั้งในฐานะที่เป็นequals()และtoString()ถูกแทนที่สำหรับชั้นเรียน (และลึก) ของวัตถุที่เปรียบเทียบ

มันมีความสำคัญเนื่องจากการทดสอบความเท่าเทียมกันในการยืนยันนั้นอาศัยequals()และข้อความทดสอบความล้มเหลวนั้นขึ้นอยู่toString()กับวัตถุที่เปรียบเทียบ
สำหรับในตัวชั้นเรียนเช่นString, Integerและอื่น ๆ สำหรับปัญหา ... เป็นแทนที่เหล่านี้ไม่มีทั้งสองและequals() toString()ดังนั้นจึงเป็นสิ่งที่ถูกต้องสมบูรณ์ที่จะยืนยันList<String>หรือกับList<Integer> และเกี่ยวกับเรื่องนี้: คุณต้องแทนที่ในชั้นเรียนเพราะมันสมเหตุสมผลในแง่ของความเสมอภาคของวัตถุไม่ใช่เพียงเพื่อให้การยืนยันง่ายขึ้นในการทดสอบกับ JUnit เพื่อให้การยืนยันง่ายขึ้นคุณมีวิธีอื่น ตามแนวทางปฏิบัติที่ดีฉันชอบห้องสมุดการยืนยัน / การจับคู่ assertEquals(Object,Object)
equals()

นี่คือวิธีแก้ปัญหา AssertJ

org.assertj.core.api.ListAssert.containsExactly() คือสิ่งที่คุณต้องการ: มันจะตรวจสอบว่ากลุ่มจริงมีค่าที่กำหนดอย่างแน่นอนและไม่มีอะไรอื่นตามที่ระบุไว้ใน javadoc

สมมติFooคลาสที่คุณเพิ่มอิลิเมนต์และที่ที่คุณสามารถรับได้
การทดสอบหน่วยของFooยืนยันว่าทั้งสองรายการมีเนื้อหาเหมือนกันอาจมีลักษณะดังนี้:

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception { 
   Foo foo = new Foo();
   foo.add("One", "Two", "Three");
   Assertions.assertThat(foo.getElements())
             .containsExactly("One", "Two", "Three");
}

จุดที่ดีของ AssertJ ก็คือการประกาศ a Listอย่างที่คาดไว้นั้นไม่จำเป็น: มันทำให้การยืนยันตรงและโค้ดอ่านง่ายขึ้น:

Assertions.assertThat(foo.getElements())
         .containsExactly("One", "Two", "Three");

แต่ห้องสมุด Assertion / matcher นั้นเป็นสิ่งที่ต้องทำเพราะสิ่งเหล่านี้จะเพิ่มมากขึ้น
สมมติว่าตอนนี้Foo ไม่ได้เก็บStrings แต่Barอินสแตนซ์ของ
นั่นคือความต้องการที่พบบ่อยมาก ด้วย AssertJ การยืนยันยังง่ายต่อการเขียน ดีกว่าคุณสามารถยืนยันว่าเนื้อหารายการเท่ากันแม้ว่าคลาสขององค์ประกอบจะไม่แทนที่equals()/hashCode()ในขณะที่วิธี JUnit ต้องการ:

import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception {
    Foo foo = new Foo();
    foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
    Assertions.assertThat(foo.getElements())
              .extracting(Bar::getId, Bar::getName)
              .containsExactly(tuple(1, "One"),
                               tuple(2, "Two"),
                               tuple(3, "Three"));
}

7

หากคุณไม่สนใจคำสั่งขององค์ประกอบฉันแนะนำ ListAssert.assertEqualsใน junit-addons

Link: http://junit-addons.sourceforge.net/

สำหรับผู้ใช้ Maven ขี้เกียจ:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

7
หมายเหตุ: หากคุณไม่สนใจเกี่ยวกับลำดับขององค์ประกอบคุณควรใช้ Set หรือ Collection ไม่ใช่ List
Barett

11
ฉันเห็นด้วย. ห้องสมุดนี้มีรายได้รวม ทำไมบนโลกจะ ListAssert.assertEquals () เริ่มต้นที่ไม่มีระเบียบ?
Ryan

5

คุณสามารถใช้assertEqualsในJunit

import org.junit.Assert;   
import org.junit.Test;

    @Test
    public void test_array_pass()
    {
        List<String> actual = Arrays.asList("fee", "fi", "foe");
        List<String> expected = Arrays.asList("fee", "fi", "foe");
        Assert.assertEquals(actual,expected);
    }

หากลำดับขององค์ประกอบแตกต่างจากนั้นจะส่งคืนข้อผิดพลาด

หากคุณยืนยันรายการวัตถุแบบจำลองคุณควรแทนที่วิธีการเท่ากับในรูปแบบเฉพาะ

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj instanceof ModelName) {
            ModelName other = (ModelName) obj;
            return this.getItem().equals(other.getItem()) ;
        }
        return false;
    }

4

หากคุณไม่ต้องการสร้างรายการอาร์เรย์คุณสามารถลองทำสิ่งนี้ได้เช่นกัน

@Test
public void test_array_pass()
{
  List<String> list = Arrays.asList("fee", "fi", "foe");
  Strint listToString = list.toString();
  Assert.assertTrue(listToString.contains("[fee, fi, foe]"));   // passes  
}

3
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));

1

อย่าประดิษฐ์ล้อใหม่!

มีห้องสมุด Google Code ที่ทำสิ่งนี้เพื่อคุณ: Hamcrest

[Hamcrest] จัดเตรียมไลบรารีของวัตถุตัวจับคู่ (หรือที่เรียกว่าข้อ จำกัด หรือภาคแสดง) ที่อนุญาตให้กำหนดกฎ 'จับคู่' ไว้อย่างชัดเจนเพื่อใช้ในกรอบงานอื่น สถานการณ์โดยทั่วไปรวมถึงกรอบการทดสอบ, การจำลองไลบรารีและกฎการตรวจสอบ UI


0

ฉันรู้ว่ามีตัวเลือกมากมายในการแก้ปัญหานี้ แต่ฉันอยากจะทำสิ่งต่อไปนี้เพื่อยืนยันสองรายการในรายการอื่น :

assertTrue(result.containsAll(expected) && expected.containsAll(result))

สิ่งนี้จะไม่ล้มเหลวหรือไม่หากคำสั่งซื้อไม่ตรงกัน
iec2011007

0

assertEquals(expected, result);ทำงานได้สำหรับฉัน เนื่องจากฟังก์ชั่นนี้รับวัตถุสองชิ้นคุณจึงสามารถผ่านสิ่งใดไปได้

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

-1

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

แต่ถ้าเรามีรายการวัตถุขนาดเท่ากันและข้อมูลที่แตกต่างกันในระดับวัตถุวิธีการเปรียบเทียบนี้จะไม่ช่วย

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

ฉันใช้Xstream lib สำหรับแทนที่ค่าเท่ากับและ hashcode แต่เราสามารถแทนที่ค่าเท่ากับและ hashcode ได้โดยไม่ชนะ logics / การเปรียบเทียบเช่นกัน

นี่คือตัวอย่างสำหรับการอ้างอิงของคุณ

    import com.thoughtworks.xstream.XStream;

    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.List;

    class TestClass {
      private String name;
      private String id;

      public void setName(String value) {
        this.name = value;
      }

      public String getName() {
        return this.name;
      }

      public String getId() {
        return id;
      }

      public void setId(String id) {
        this.id = id;
      }

      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      @Override
      public boolean equals(Object o) {
        XStream xstream = new XStream();
        String oxml = xstream.toXML(o);
        String myxml = xstream.toXML(this);

        return myxml.equals(oxml);
      }

      /**
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        XStream xstream = new XStream();
        String myxml = xstream.toXML(this);
        return myxml.hashCode();
      }
    }

    public class XstreamCompareTest {
      public static void main(String[] args) throws ParseException {
      checkObjectEquals();
}

      private static void checkObjectEquals() {
        List<TestClass> testList1 = new ArrayList<TestClass>();
        TestClass tObj1 = new TestClass();
        tObj1.setId("test3");
        tObj1.setName("testname3");
        testList1.add(tObj1);

        TestClass tObj2 = new TestClass();
        tObj2.setId("test2");
        tObj2.setName("testname2");
        testList1.add(tObj2);

        testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        List<TestClass> testList2 = new ArrayList<TestClass>();
        TestClass tObj3 = new TestClass();
        tObj3.setId("test3");
        tObj3.setName("testname3");
        testList2.add(tObj3);

        TestClass tObj4 = new TestClass();
        tObj4.setId("test2");
        tObj4.setName("testname2");
        testList2.add(tObj4);

        testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        if (isNotMatch(testList1, testList2)) {
          System.out.println("The list are not matched");
        } else {
          System.out.println("The list are matched");
        }

      }

      private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
        return clist1.size() != clist2.size() || !clist1.equals(clist2);
      }
    }

สิ่งที่สำคัญที่สุดคือคุณสามารถละเว้นฟิลด์ด้วย Annotation (@XStreamOmitField) หากคุณไม่ต้องการรวมฟิลด์ใด ๆ ในการตรวจสอบวัตถุที่เท่ากัน มีคำอธิบายประกอบมากมายเช่นนี้เพื่อกำหนดค่าดังนั้นให้มองลึกลงไปเกี่ยวกับคำอธิบายประกอบของ lib นี้

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

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