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


112

สมมติว่าฉันมีคลาสที่ไม่มีเมธอดเท่ากับ () ซึ่งไม่มีแหล่งที่มา ฉันต้องการยืนยันความเท่าเทียมกันในสองอินสแตนซ์ของคลาสนั้น

ฉันสามารถยืนยันได้หลายข้อ:

assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...

ฉันไม่ชอบวิธีแก้ปัญหานี้เพราะฉันไม่ได้รับภาพความเท่าเทียมกันทั้งหมดหากการยืนยันในช่วงต้นล้มเหลว

ฉันสามารถเปรียบเทียบด้วยตนเองและติดตามผล:

String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
    errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
    errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);

สิ่งนี้ทำให้ฉันเห็นภาพความเท่าเทียมกันเต็มรูปแบบ แต่เป็นเรื่องที่ยุ่งเหยิง (และฉันไม่ได้คิดถึงปัญหาว่างที่เป็นไปได้) ตัวเลือกที่สามคือการใช้ตัวเปรียบเทียบ แต่ CompareTo () จะไม่บอกฉันว่าฟิลด์ใดล้มเหลวในความเท่าเทียมกัน

มีแนวทางปฏิบัติที่ดีกว่าไหมที่จะได้รับสิ่งที่ฉันต้องการจากวัตถุโดยไม่ต้องคลาสย่อยและการเขียนทับเท่ากับ (ฮึ)


คุณกำลังมองหาห้องสมุดที่เปรียบเทียบได้อย่างลึกซึ้งสำหรับคุณหรือไม่? เช่น deep-equals แนะนำที่stackoverflow.com/questions/1449001/… ?
Vikdor

3
ทำไมคุณต้องรู้ว่าทำไมอินสแตนซ์ทั้งสองไม่เท่ากัน โดยปกติการใช้งานequalเมธอดจะบอกได้ว่าสองอินสแตนซ์เท่ากันหรือไม่และเราไม่สนใจว่าทำไมอินสแตนซ์ไม่เท่ากัน
Bhesh Gurung

3
อยากทราบว่าคุณสมบัติอะไรไม่เท่ากันจะได้แก้ไขได้ :)
Ryan Nelson

ทั้งหมดObjectมีequalsวิธีการคุณอาจหมายถึงไม่มีวิธีการเท่ากับที่ถูกแทนที่
Steve Kuo

วิธีที่ดีที่สุดที่ฉันคิดได้คือใช้คลาส wrapper หรือคลาสย่อยแล้วใช้หลังจากแทนที่วิธีการเท่ากับ ..
Thihara

คำตอบ:


67

Mockitoเสนอตัวจับคู่ภาพสะท้อน:

สำหรับรุ่นล่าสุดของการใช้ Mockito:

Assert.assertTrue(new ReflectionEquals(expected, excludeFields).matches(actual));

สำหรับเวอร์ชันเก่าให้ใช้:

Assert.assertThat(actual, new ReflectionEquals(expected, excludeFields));

17
org.mockito.internal.matchers.apachecommonsชั้นนี้อยู่ในแพคเกจ สถานะเอกสาร Mockito: org.mockito.internal-> "คลาสภายในไม่ให้ไคลเอ็นต์ใช้" คุณจะทำให้โครงการของคุณตกอยู่ในความเสี่ยงโดยใช้สิ่งนี้ สิ่งนี้สามารถเปลี่ยนแปลงได้ในรุ่น Mockito ใด ๆ อ่านที่นี่: site.mockito.org/mockito/docs/current/overview-summary.html
luboskrnac

6
ใช้Mockito.refEq()แทน
Jeremy Kao

1
Mockito.refEq()ล้มเหลวเมื่อวัตถุไม่มี id set = (
cavpollo

1
@PiotrAleksanderChmielowski ขออภัยเมื่อทำงานกับ Spring + JPA + Entities อ็อบเจ็กต์เอนทิตีอาจมี id (แสดงถึงฟิลด์ id ของตารางฐานข้อมูล) ดังนั้นเมื่อว่าง (อ็อบเจ็กต์ใหม่ที่ยังไม่ได้เก็บบน DB) จะrefEqล้มเหลว เพื่อเปรียบเทียบเนื่องจากวิธีการแฮชโค้ดไม่สามารถเปรียบเทียบวัตถุได้
cavpollo

3
มันใช้งานได้ดี แต่คาดว่าจริงและผิดลำดับ มันควรจะเป็นวิธีอื่น ๆ
pkawiak

48

มีคำตอบที่ถูกต้องมากมายที่นี่ แต่ฉันต้องการเพิ่มเวอร์ชันของฉันด้วย โดยอ้างอิงจาก Assertj

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

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .isEqualToComparingFieldByFieldRecursively(expectedObject);
    }
}

UPDATE: ใน assertj v3.13.2 วิธีนี้เลิกใช้แล้วตามที่ Woodz ชี้ไว้ในความคิดเห็น คำแนะนำปัจจุบันคือ

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .usingRecursiveComparison()
            .isEqualTo(expectedObject);
    }

}

3
ใน assertj v3.13.2 วิธีนี้เลิกใช้แล้วและตอนนี้คำแนะนำจะใช้usingRecursiveComparison()กับisEqualTo()บรรทัดคือassertThat(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);
Woodz

46

โดยทั่วไปฉันใช้ usecase นี้โดยใช้ org.apache.commons.lang3.builder.EqualsBuilder

Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));

1
Gradle: androidTestCompile 'org.apache.commons: commons-lang3: 3.5'
Roel

1
คุณต้องเพิ่มสิ่งนี้ลงในไฟล์ gradle ของคุณภายใต้ "การอ้างอิง" เมื่อคุณต้องการใช้ "org.apache.commons.lang3.builder.EqualsBuilder"
Roel

3
สิ่งนี้ไม่ได้ให้คำแนะนำใด ๆ ว่าฟิลด์ใดที่ไม่ตรงกับความเป็นจริง
Vadzim

1
@Vadzim ฉันได้ใช้รหัสด้านล่างเพื่อรับ Assert.assertEquals (ReflectionToStringBuilder.toString (คาดว่า), ReflectionToStringBuilder.toString (จริง));
Abhijeet Kushe

2
อันนี้ต้องการโหนดทั้งหมดในกราฟใช้ "เท่ากัน" และ "แฮชโค้ด" ซึ่งโดยพื้นฐานแล้ววิธีนี้แทบจะไร้ประโยชน์ isEqualToComparingFieldByFieldRecursively ของ AssertJ เป็นสิ่งที่ทำงานได้อย่างสมบูรณ์แบบในกรณีของฉัน
John Zhang

12

ฉันรู้ว่ามันเก่าไปหน่อย แต่ฉันหวังว่ามันจะช่วยได้

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

คำตอบที่ได้รับการโหวตมากที่สุด (ไม่ใช่คำตอบที่ผู้เขียนเลือก) สำหรับคำถามที่คล้ายกันนี้เป็นคำตอบที่เหมาะสมที่สุดสำหรับคุณ

โดยทั่วไปจะประกอบด้วยการใช้ไลบรารีที่เรียกว่าUnitils Unitils

นี่คือการใช้:

User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

ซึ่งจะผ่านไปแม้ว่าคลาสUserจะไม่ได้ใช้equals()ก็ตาม คุณสามารถดูตัวอย่างเพิ่มเติมและยืนยันเย็นจริงๆเรียกว่าassertLenientEqualsพวกเขาในการกวดวิชา


1
แต่น่าเสียดายที่Unitilsดูเหมือนว่าจะมีผู้เสียชีวิตเห็นstackoverflow.com/questions/34658067/is-unitils-project-alive
Ken Williams

8

คุณสามารถใช้ได้ Apache commons lang ReflectionToStringBuilder

คุณสามารถระบุแอตทริบิวต์ที่คุณต้องการทดสอบทีละรายการหรือดีกว่าโดยไม่รวมแอตทริบิวต์ที่คุณไม่ต้องการ:

String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE)
                .setExcludeFieldNames(new String[] { "foo", "bar" }).toString()

จากนั้นคุณเปรียบเทียบทั้งสองสายตามปกติ สำหรับประเด็นเกี่ยวกับการสะท้อนที่ช้าฉันคิดว่านี่เป็นเพียงการทดสอบเท่านั้นดังนั้นไม่ควรสำคัญมาก


1
ประโยชน์เพิ่มเติมของแนวทางนี้คือคุณจะได้รับเอาต์พุตภาพที่แสดงค่าที่คาดหวังและค่าจริงเป็นสตริงที่ไม่รวมฟิลด์ที่คุณไม่สนใจ
fquinner

7

หากคุณกำลังใช้แฮมเครสต์สำหรับการยืนยันของคุณ (assertThat) และไม่ต้องการดึง libs ทดสอบเพิ่มเติมคุณสามารถใช้ SamePropertyValuesAs.samePropertyValuesAsเพื่อยืนยันรายการที่ไม่มีวิธีการเท่ากับที่ถูกแทนที่

ข้อดีคือคุณไม่จำเป็นต้องดึงกรอบการทดสอบอื่นเข้ามาและจะให้ข้อผิดพลาดที่เป็นประโยชน์เมื่อการยืนยันล้มเหลว ( expected: field=<value> but was field=<something else>) แทนที่จะexpected: true but was falseใช้สิ่งที่ต้องการEqualsBuilder.reflectionEquals()ถ้าคุณใช้สิ่งที่ต้องการ

ข้อเสียคือเป็นการเปรียบเทียบแบบตื้น ๆ และไม่มีตัวเลือกสำหรับการยกเว้นฟิลด์ (เช่นเดียวกับใน EqualsBuilder) ดังนั้นคุณจะต้องจัดการกับวัตถุที่ซ้อนกัน (เช่นลบออกและเปรียบเทียบอย่างอิสระ)

กรณีที่ดีที่สุด:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
assertThat(actual, is(samePropertyValuesAs(expected)));

กรณีที่น่าเกลียด:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
SomeClass expected = buildExpected(); 
SomeClass actual = sut.doSomething();

assertThat(actual.getSubObject(), is(samePropertyValuesAs(expected.getSubObject())));    
expected.setSubObject(null);
actual.setSubObject(null);

assertThat(actual, is(samePropertyValuesAs(expected)));

ดังนั้นเลือกพิษของคุณ เฟรมเวิร์กเพิ่มเติม (เช่น Unitils) ข้อผิดพลาดที่ไม่เป็นประโยชน์ (เช่น EqualsBuilder) หรือการเปรียบเทียบแบบตื้น (hamcrest)


1
สำหรับการทำงานSamePropertyValuesAsคุณต้องเพิ่มในโครงการ dependecy hamcrest.org/JavaHamcrest/…
bigspawn

ฉันชอบโซลูชันนี้เนื่องจากไม่ได้เพิ่ม "การพึ่งพาเพิ่มเติมที่แตกต่างกันโดยสิ้นเชิง!
GhostCat


2

วิธีการเปรียบเทียบการสะท้อนบางส่วนนั้นตื้น

อีกทางเลือกหนึ่งคือการแปลงวัตถุเป็น json และเปรียบเทียบสตริง

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;    
public static String getJsonString(Object obj) {
 try {
    ObjectMapper objectMapper = new ObjectMapper();
    return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
     } catch (JsonProcessingException e) {
        LOGGER.error("Error parsing log entry", e);
        return null;
    }
}
...
assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject))

2

ใช้Shazamcrestคุณสามารถทำได้:

assertThat(obj1, sameBeanAs(obj2));

ทำไมต้องใช้ shazamcrest แทน hamcrest? stackoverflow.com/a/27817702/6648326
MasterJoe

1
@ MasterJoe2 ในการทดสอบของฉันreflectEqualsจะส่งคืนfalseเมื่อวัตถุมีการอ้างอิงที่แตกต่างกัน
Leonel Sanches da Silva

1

เปรียบเทียบฟิลด์ต่อฟิลด์:

assertNotNull("Object 1 is null", obj1);
assertNotNull("Object 2 is null", obj2);
assertEquals("Field A differs", obj1.getFieldA(), obj2.getFieldA());
assertEquals("Field B differs", obj1.getFieldB(), obj2.getFieldB());
...
assertEquals("Objects are not equal.", obj1, obj2);

1
นี่เป็นสิ่งที่ฉันไม่ต้องการทำเพราะความล้มเหลวในการยืนยัน แต่เนิ่นๆจะซ่อนความล้มเหลวที่อาจเกิดขึ้นได้ด้านล่าง
Ryan Nelson

2
ขออภัยฉันพลาดส่วนนั้นในโพสต์ของคุณ ... เหตุใด "ภาพความเท่าเทียมกันทั้งหมด" จึงมีความสำคัญในสภาพแวดล้อมการทดสอบหน่วยการเรียนรู้ ฟิลด์ทั้งหมดเท่ากัน (ผ่านการทดสอบ) หรือไม่เท่ากันทั้งหมด (การทดสอบล้มเหลว)
EthanB

ฉันไม่ต้องการที่จะต้องรันการทดสอบใหม่เพื่อดูว่าฟิลด์อื่นไม่เท่ากันหรือไม่ ฉันต้องการทราบข้อมูลล่วงหน้าทั้งหมดที่ไม่เท่ากันเพื่อที่ฉันจะได้จัดการได้ในครั้งเดียว
Ryan Nelson

1
การยืนยันหลายฟิลด์ในการทดสอบเดียวจะไม่ถือว่าเป็นการทดสอบ 'หน่วย' ที่แท้จริง ด้วยการพัฒนาที่ขับเคลื่อนด้วยการทดสอบแบบเดิม (TDD) คุณจะต้องเขียนการทดสอบขนาดเล็กจากนั้นจึงใส่โค้ดเพียงอย่างเดียวเพียงพอที่จะผ่าน การยืนยันหนึ่งครั้งต่อเขตข้อมูลเป็นวิธีที่ถูกต้องในการทำเพียงอย่าใส่การยืนยันทั้งหมดลงในการทดสอบเดียว สร้างการทดสอบที่แตกต่างกันสำหรับการยืนยันแต่ละสนามที่คุณสนใจ สิ่งนี้จะช่วยให้คุณเห็นข้อผิดพลาดทั้งหมดในฟิลด์ทั้งหมดด้วยการเรียกใช้ชุดเดียว หากทำได้ยากอาจหมายความว่าโค้ดของคุณไม่ได้เป็นโมดูลที่เพียงพอในตอนแรกและอาจถูกปรับโครงสร้างใหม่ให้เป็นโซลูชันที่สะอาดกว่า
Jesse Webb

นี่เป็นคำแนะนำที่ถูกต้องและเป็นวิธีปกติที่คุณจะแก้ปัญหาการยืนยันหลายรายการในการทดสอบครั้งเดียว ความท้าทายเดียวคือฉันต้องการมุมมองแบบองค์รวมของวัตถุ นั่นคือฉันต้องการทดสอบทุกช่องพร้อมกันเพื่อตรวจสอบว่าวัตถุอยู่ในสถานะที่ถูกต้อง นี่ไม่ใช่เรื่องยากที่จะทำเมื่อคุณมีเมธอด overridden equals () (ซึ่งแน่นอนฉันไม่มีในตัวอย่างนี้)
Ryan Nelson

1

การยืนยัน AssertJ สามารถใช้เพื่อเปรียบเทียบค่าโดยไม่มี#equalsวิธีการแทนที่อย่างถูกต้องเช่น:

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

// ...

assertThat(actual)
    .usingRecursiveComparison()
    .isEqualTo(expected);

1

เนื่องจากคำถามนี้เก่าแล้วฉันจะแนะนำแนวทางที่ทันสมัยอีกวิธีหนึ่งโดยใช้ JUnit 5

ฉันไม่ชอบวิธีแก้ปัญหานี้เพราะฉันไม่ได้รับภาพความเท่าเทียมกันทั้งหมดหากการยืนยันในช่วงต้นล้มเหลว

ด้วย JUnit 5 มีวิธีการที่เรียกว่าAssertions.assertAll()ซึ่งจะช่วยให้คุณสามารถจัดกลุ่มการยืนยันทั้งหมดในการทดสอบของคุณเข้าด้วยกันและจะดำเนินการแต่ละรายการและแสดงผลการยืนยันที่ล้มเหลวในตอนท้าย ซึ่งหมายความว่าการยืนยันใด ๆ ที่ล้มเหลวก่อนจะไม่หยุดการดำเนินการของการยืนยันในภายหลัง

assertAll("Test obj1 with obj2 equality",
    () -> assertEquals(obj1.getFieldA(), obj2.getFieldA()),
    () -> assertEquals(obj1.getFieldB(), obj2.getFieldB()),
    () -> assertEquals(obj1.getFieldC(), obj2.getFieldC()));

0

คุณสามารถใช้การสะท้อนเพื่อ "ทำการทดสอบความเท่าเทียมกันโดยอัตโนมัติ" ได้ คุณสามารถใช้โค้ด "ติดตาม" ความเท่าเทียมกันที่คุณเขียนไว้สำหรับฟิลด์เดียวจากนั้นใช้การสะท้อนกลับเพื่อเรียกใช้การทดสอบนั้นกับทุกฟิลด์ในออบเจ็กต์


0

นี่เป็นวิธีการเปรียบเทียบทั่วไปที่เปรียบเทียบวัตถุสองชิ้นในคลาสเดียวกันสำหรับค่าของฟิลด์ (โปรดทราบว่าสามารถเข้าถึงได้โดยใช้เมธอด get)

public static <T> void compare(T a, T b) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    AssertionError error = null;
    Class A = a.getClass();
    Class B = a.getClass();
    for (Method mA : A.getDeclaredMethods()) {
        if (mA.getName().startsWith("get")) {
            Method mB = B.getMethod(mA.getName(),null );
            try {
                Assert.assertEquals("Not Matched = ",mA.invoke(a),mB.invoke(b));
            }catch (AssertionError e){
                if(error==null){
                    error = new AssertionError(e);
                }
                else {
                    error.addSuppressed(e);
                }
            }
        }
    }
    if(error!=null){
        throw error ;
    }
}

0

ฉันสะดุดกับกรณีที่คล้ายกันมาก

ผมอยากจะเปรียบเทียบในการทดสอบว่าวัตถุมีค่าแอตทริบิวต์เช่นเดียวกับอีกคนหนึ่ง แต่วิธีการเช่นis(), refEq()ฯลฯ จะไม่ทำงานด้วยเหตุผลเช่นวัตถุของฉันมีค่า null ในของมันidแอตทริบิวต์

นี่คือวิธีแก้ปัญหาที่ฉันพบ (เพื่อนร่วมงานพบ):

import static org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare;

assertThat(reflectionCompare(expectedObject, actualObject, new String[]{"fields","to","be","excluded"}), is(0));

ถ้าค่าที่ได้จากreflectionCompare0 แสดงว่ามีค่าเท่ากัน ถ้าเป็น -1 หรือ 1 จะแตกต่างกันในบางแอตทริบิวต์


0

ในกรณีทั่วไปของ AssertJ คุณสามารถสร้างกลยุทธ์เปรียบเทียบแบบกำหนดเอง:

assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam)
assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);

ใช้กลยุทธ์การเปรียบเทียบแบบกำหนดเองในการยืนยัน

AssertJ ตัวอย่าง


0

ฉันมีปริศนาที่เหมือนกันทุกประการเมื่อหน่วยทดสอบแอป Android และวิธีแก้ปัญหาที่ง่ายที่สุดที่ฉันคิดขึ้นมาคือใช้Gsonเพื่อแปลงวัตถุมูลค่าจริงและที่คาดหวังของฉันjsonและเปรียบเทียบเป็นสตริง

String actual = new Gson().toJson( myObj.getValues() );
String expected = new Gson().toJson( new MyValues(true,1) );

assertEquals(expected, actual);

ข้อดีของสิ่งนี้ในการเปรียบเทียบฟิลด์ต่อฟิลด์ด้วยตนเองคือคุณเปรียบเทียบฟิลด์ทั้งหมดของคุณดังนั้นแม้ว่าคุณจะเพิ่มฟิลด์ใหม่ในชั้นเรียนของคุณในภายหลัง แต่ก็จะได้รับการทดสอบโดยอัตโนมัติเมื่อเทียบกับถ้าคุณใช้assertEquals()บน ฟิลด์ทั้งหมดซึ่งจะต้องได้รับการอัปเดตหากคุณเพิ่มฟิลด์เพิ่มเติมในชั้นเรียนของคุณ

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


1
Gson ไม่รับประกันคำสั่งซื้อช่อง คุณอาจต้องการ Json แยกสตริงและเปรียบเทียบ JsonElements ที่เป็นผลมาจากการแยกวิเคราะห์
ozma

0

ฉันลองหาคำตอบทั้งหมดแล้วและไม่มีอะไรได้ผลสำหรับฉันเลย

ฉันจึงสร้างวิธีการของตัวเองที่เปรียบเทียบวัตถุ java อย่างง่ายโดยไม่ต้องเจาะลึกลงไปในโครงสร้างที่ซ้อนกัน ...

เมธอดจะคืนค่า null หากฟิลด์ทั้งหมดตรงกันหรือสตริงที่มีรายละเอียดไม่ตรงกัน

เปรียบเทียบคุณสมบัติที่มีเมธอด getter เท่านั้น

วิธีใช้

        assertNull(TestUtils.diff(obj1,obj2,ignore_field1, ignore_field2));

เอาต์พุตตัวอย่างหากมีความไม่ตรงกัน

เอาต์พุตแสดงชื่อคุณสมบัติและค่าตามลำดับของออบเจ็กต์ที่เปรียบเทียบ

alert_id(1:2), city(Moscow:London)

รหัส (Java 8 ขึ้นไป):

 public static String diff(Object x1, Object x2, String ... ignored) throws Exception{
        final StringBuilder response = new StringBuilder();
        for (Method m:Arrays.stream(x1.getClass().getMethods()).filter(m->m.getName().startsWith("get")
        && m.getParameterCount()==0).collect(toList())){

            final String field = m.getName().substring(3).toLowerCase();
            if (Arrays.stream(ignored).map(x->x.toLowerCase()).noneMatch(ignoredField->ignoredField.equals(field))){
                Object v1 = m.invoke(x1);
                Object v2 = m.invoke(x2);
                if ( (v1!=null && !v1.equals(v2)) || (v2!=null && !v2.equals(v1))){
                    response.append(field).append("(").append(v1).append(":").append(v2).append(")").append(", ");
                }
            }
        }
        return response.length()==0?null:response.substring(0,response.length()-2);
    }

0

อีกทางเลือกหนึ่งสำหรับ junit-only คุณสามารถตั้งค่าฟิลด์เป็น null ก่อนที่จะเท่ากับการยืนยัน:

    actual.setCreatedDate(null); // excludes date assertion
    expected.setCreatedDate(null);

    assertEquals(expected, actual);

-1

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

public static String findDifference(Type obj1, Type obj2) {
    String difference = "";
    if (obj1.getFieldA() == null && obj2.getFieldA() != null
            || !obj1.getFieldA().equals(obj2.getFieldA())) {
        difference += "Difference at field A:" + "obj1 - "
                + obj1.getFieldA() + ", obj2 - " + obj2.getFieldA();
    }
    if (obj1.getFieldB() == null && obj2.getFieldB() != null
            || !obj1.getFieldB().equals(obj2.getFieldB())) {
        difference += "Difference at field B:" + "obj1 - "
                + obj1.getFieldB() + ", obj2 - " + obj2.getFieldB();
        // (...)
    }
    return difference;
}

คุณสามารถใช้วิธีนี้ใน JUnit ได้ดังนี้:

assertEquals ("วัตถุไม่เท่ากัน", "", findDifferences (obj1, obj));

ซึ่งไม่น่าเบื่อและให้ข้อมูลที่ครบถ้วนเกี่ยวกับความแตกต่างหากมีอยู่ (โดยไม่ได้อยู่ในรูปแบบปกติของ assertEqual แต่คุณจะได้รับข้อมูลทั้งหมดจึงน่าจะดี)


-1

สิ่งนี้จะไม่ช่วย OP แต่อาจช่วยนักพัฒนา C # ที่ลงเอยที่นี่ ...

เช่นเดียวกับที่Enrique โพสต์คุณควรลบล้างวิธีการเท่ากับ

มีแนวทางปฏิบัติที่ดีกว่าไหมที่จะได้รับสิ่งที่ฉันต้องการจากวัตถุโดยไม่ต้องคลาสย่อยและการเขียนทับเท่ากับ (ฮึ)

คำแนะนำของฉันคืออย่าใช้คลาสย่อย ใช้คลาสบางส่วน

คำจำกัดความของคลาสบางส่วน (MSDN)

ดังนั้นชั้นเรียนของคุณจะดูเหมือน ...

public partial class TheClass
{
    public override bool Equals(Object obj)
    {
        // your implementation here
    }
}

สำหรับ Java ฉันเห็นด้วยกับข้อเสนอแนะในการใช้การสะท้อน เพียงจำไว้ว่าคุณควรหลีกเลี่ยงการใช้การสะท้อนทุกครั้งที่ทำได้ มันช้ายากที่จะดีบักและยิ่งยากที่จะรักษาในอนาคตเพราะ IDE อาจทำลายรหัสของคุณได้โดยการเปลี่ยนชื่อฟิลด์หรืออะไรทำนองนั้น ระวัง!


-1

จากความคิดเห็นของคุณไปจนถึงคำตอบอื่น ๆ ฉันไม่เข้าใจว่าคุณต้องการอะไร

เพียงเพื่อประโยชน์ในการสนทนาสมมติว่าชั้นเรียนได้ลบล้างวิธีการเท่ากับ

ดังนั้น UT ของคุณจะมีลักษณะดังนี้:

SomeType expected = // bla
SomeType actual = // bli

Assert.assertEquals(expected, actual). 

และคุณทำเสร็จแล้ว ยิ่งไปกว่านั้นคุณไม่สามารถรับ "ภาพความเท่าเทียมกัน" ได้หากการยืนยันล้มเหลว

จากสิ่งที่ฉันเข้าใจคุณกำลังบอกว่าแม้ว่าประเภทจะลบล้างเท่ากับคุณจะไม่สนใจเพราะคุณต้องการได้รับ "ภาพความเท่าเทียมเต็ม" ดังนั้นจึงไม่มีจุดที่จะขยายและลบล้างความเท่าเทียมกัน

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

ตัวอย่างเช่นคุณสามารถสร้างคลาสตัวช่วยที่ทำให้เป็นอนุกรมของประเภทที่คุณต้องการเปรียบเทียบกับเอกสาร XML และเปรียบเทียบกับ XML ที่ได้! ในกรณีนี้คุณสามารถมองเห็นได้ด้วยสายตาว่าอะไรเท่ากันและอะไรไม่เท่ากัน

วิธีนี้จะช่วยให้คุณมีโอกาสดูภาพเต็ม แต่ก็ค่อนข้างยุ่งยากเช่นกัน (และมีข้อผิดพลาดเล็กน้อยในตอนแรก)


เป็นไปได้ว่าคำว่า "ภาพความเท่าเทียมเต็ม" ของฉันอาจทำให้สับสน การใช้งานเท่ากับ () จะช่วยแก้ปัญหาได้ ฉันสนใจที่จะรู้เขตข้อมูลที่ไม่เท่ากันทั้งหมด (เกี่ยวข้องกับความเท่าเทียมกัน) ในเวลาเดียวกันโดยไม่ต้องทำการทดสอบซ้ำ การทำให้วัตถุเป็นอนุกรมเป็นไปได้อีกอย่างหนึ่ง แต่ฉันไม่จำเป็นต้องมีค่าเท่ากับลึก ฉันต้องการใช้การใช้งาน equals () ของคุณสมบัติถ้าเป็นไปได้
Ryan Nelson

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

-3

คุณสามารถแทนที่วิธีการเท่ากับของคลาสเช่น:

@Override
public int hashCode() {
    int hash = 0;
    hash += (app != null ? app.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    HubRule other = (HubRule) object;

    if (this.app.equals(other.app)) {
        boolean operatorHubList = false;

        if (other.operator != null ? this.operator != null ? this.operator
                .equals(other.operator) : false : true) {
            operatorHubList = true;
        }

        if (operatorHubList) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

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


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