[java] equals 메소드없이 두 클래스에서 동등성을 어떻게 주장합니까?

소스가없는 equals () 메서드가없는 클래스가 있다고 가정 해 보겠습니다. 해당 클래스의 두 인스턴스에 대해 동등성을 주장하고 싶습니다.

여러 단언을 할 수 있습니다.

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

이것은 나에게 완전한 평등 그림을 제공하지만 투박합니다 (그리고 가능한 null 문제에 대해서도 설명하지 않았습니다). 세 번째 옵션은 Comparator를 사용하는 것이지만 compareTo ()는 어떤 필드가 동일하지 않은지 알려주지 않습니다.

하위 클래스를 지정하고 같음 (ugh)을 재정의하지 않고 객체에서 원하는 것을 얻는 더 나은 방법이 있습니까?



답변

Mockito 는 리플렉션 매칭 기를 제공합니다.

최신 버전의 Mockito 사용 :

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

이전 버전의 경우 다음을 사용하십시오.

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


답변

여기에 많은 정답이 있지만 내 버전도 추가하고 싶습니다. 이것은 Assertj를 기반으로합니다.

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

public class TestClass {

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

업데이트 : assertj v3.13.2에서이 메서드는 Woodz가 주석에서 지적한 것처럼 더 이상 사용되지 않습니다. 현재 권장 사항은

public class TestClass {

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

}


답변

저는 일반적으로 org.apache.commons.lang3.builder.EqualsBuilder를 사용하여이 사용 사례를 구현합니다.

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


답변

조금 오래되었다는 것을 알고 있지만 도움이 되었으면합니다.

나는 당신과 똑같은 문제에 부딪 혔기 때문에 조사 끝에 이것과 비슷한 질문을 거의 발견하지 못했고, 해결책을 찾은 후에 나는 다른 사람들을 도울 수 있다고 생각했기 때문에 그 질문에 똑같이 대답했습니다.

유사한 질문에 대해 가장 많이 득표 한 답변 (저자가 선택한 답변이 아님)이 가장 적합한 솔루션입니다.

기본적으로 Unitils 라는 라이브러리를 사용하여 구성됩니다 .

이것이 사용입니다.

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

클래스 User가 구현하지 않아도 통과 합니다 equals(). 더 많은 예제와 assertLenientEquals그들의 튜토리얼 에서 호출되는 정말 멋진 어설 션을 볼 수 있습니다 .


답변

Apache commons lang ReflectionToStringBuilder를 사용할 수 있습니다.

테스트 할 속성을 하나씩 지정하거나 원하지 않는 속성을 제외하는 것이 좋습니다.

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

그런 다음 두 문자열을 정상적으로 비교합니다. 반사가 느리다는 점에 대해서는 이것이 테스트 용이라고 가정하므로 그렇게 중요하지 않아야합니다.


답변

어설 션 (assertThat)에 hamcrest를 사용하고 있고 추가 테스트 라이브러리를 가져 오지 않으려면을 사용 SamePropertyValuesAs.samePropertyValuesAs하여 재정의 된 equals 메서드가없는 항목을 어설 션 할 수 있습니다 .

장점은 또 다른 테스트 프레임 워크를 가져올 필요가 없으며 . 같은 것을 사용하는 expected: field=<value> but was field=<something else>대신 assert가 실패 할 때 ( ) 유용한 오류를 제공 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).


답변

라이브러리 Hamcrest 1.3 Utility Matchers 에는 같음 대신 반사를 사용하는 특수 매 처가 있습니다.

assertThat(obj1, reflectEquals(obj2));