[java] 한 단위가 hashCode-equals 계약을 어떻게 테스트해야합니까?

간단히 말해서, 자바의 object.hashCode ()에 따른 hashCode 계약 :

  1. equals ()에 영향을 미치는 것이 변경되지 않는 한 해시 코드는 변경되지 않아야합니다.
  2. equals ()는 해시 코드가 ==임을 의미합니다.

주로 불변 데이터 객체에 관심이 있다고 가정 해 봅시다. 생성 된 정보는 절대 변경되지 않으므로 # 1이 유지되는 것으로 간주됩니다. 그것은 # 2를 남깁니다. 문제는 단순히 해시 코드 ==를 의미한다는 것을 확인하는 것입니다.

분명히, 우리는 그 집합이 사소하게 작지 않는 한 모든 가능한 데이터 객체를 테스트 할 수 없습니다. 그렇다면 일반적인 경우를 포착 할 수있는 단위 테스트를 작성하는 가장 좋은 방법은 무엇입니까?

이 클래스의 인스턴스는 불변이므로 이러한 객체를 생성하는 방법은 제한되어 있습니다. 이 단위 테스트는 가능하면 모든 것을 다루어야합니다. 내 머리 꼭대기에서 진입 점은 생성자, 역 직렬화 및 하위 클래스의 생성자입니다 (생성자 호출 문제를 줄일 수 있어야 함).

[연구를 통해 내 질문에 답하려고 노력할 것입니다. 다른 StackOverflowers의 입력은이 프로세스에 대한 환영받는 안전 메커니즘입니다.]

[다른 OO 언어에도 적용 할 수 있으므로 해당 태그를 추가하겠습니다.]



답변

EqualsVerifier 는 비교적 새로운 오픈 소스 프로젝트이며 equals 계약을 테스트하는 데 매우 효과적입니다. GSBase의 EqualsTester 에는 문제 가 없습니다 . 나는 그것을 확실히 추천 할 것입니다.


답변

내 조언은 이것이 사실이 아닌 이유 / 방법을 생각한 다음 이러한 상황을 대상으로하는 단위 테스트를 작성하는 것입니다.

예를 들어 사용자 지정 Set클래스 가 있다고 가정 해 보겠습니다 . 두 세트가 동일한 요소를 포함하는 경우 동일하지만 두 세트의 기본 데이터 구조는 해당 요소가 다른 순서로 저장되는 경우 다를 수 있습니다. 예를 들면 :

MySet s1 = new MySet( new String[]{"Hello", "World"} );
MySet s2 = new MySet( new String[]{"World", "Hello"} );
assertEquals(s1, s2);
assertTrue( s1.hashCode()==s2.hashCode() );

이 경우 구현 한 해싱 알고리즘에 따라 세트의 요소 순서가 해시에 영향을 미칠 수 있습니다. 그래서 이것은 제가 작성한 테스트의 종류입니다. 왜냐하면 그것은 제가 동일하다고 정의한 두 개체에 대해 일부 해싱 알고리즘이 다른 결과를 생성 할 수 있다는 것을 알고있는 경우를 테스트하기 때문입니다.

어떤 것이 든 자신의 사용자 정의 클래스와 유사한 표준을 사용해야합니다.


답변

이를 위해 junit 애드온을 사용할 가치가 있습니다. EqualsHashCodeTestCase http://junit-addons.sourceforge.net/ 클래스를 확인하십시오. 이것을 확장하고 createInstance 및 createNotEqualInstance를 구현할 수 있습니다. 그러면 equals 및 hashCode 메서드가 올바른지 확인합니다.


답변

GSBase 의 EqualsTester 를 추천합니다 . 기본적으로 원하는 것을 수행합니다. 그래도 두 가지 (사소한) 문제가 있습니다.

  • 생성자는 모든 작업을 수행하므로 좋은 습관이라고 생각하지 않습니다.
  • 클래스 A의 인스턴스가 클래스 A의 하위 클래스의 인스턴스와 같으면 실패합니다. 이것이 반드시 같음 계약을 위반하는 것은 아닙니다.


답변

[이 글을 쓰는 시점에 3 개의 다른 답변이 게시되었습니다.]

다시 말하지만, 내 질문의 목적은 확인 시험의 표준 경우 찾을 수 있습니다 hashCodeequals서로 동의하는 것입니다. 이 질문에 대한 나의 접근 방식은 문제의 클래스, 즉 불변 데이터를 작성할 때 프로그래머가 취하는 일반적인 경로를 상상하는 것입니다. 예를 들면 :

  1. equals()작성하지 않고 hashCode(). 이것은 종종 두 인스턴스의 필드가 동등 함을 의미하는 동등성이 정의되었음을 의미합니다.
  2. hashCode()작성하지 않고 equals(). 이것은 프로그래머가보다 효율적인 해싱 알고리즘을 찾고 있음을 의미 할 수 있습니다.

# 2의 경우 문제가없는 것 같습니다. 추가 인스턴스가 만들어 equals()지지 않았으므로 동일한 해시 코드를 갖는 데 추가 인스턴스가 필요하지 않습니다. 최악의 경우 해시 알고리즘은이 질문의 범위를 벗어난 해시 맵의 성능을 저하시킬 수 있습니다.

# 1의 경우 표준 단위 테스트는 생성자에 전달 된 동일한 데이터를 사용하여 동일한 개체의 두 인스턴스를 만들고 동일한 해시 코드를 확인하는 작업을 수반합니다. 오탐은 어떻습니까? 그럼에도 불구하고 건전하지 않은 알고리즘에서 동일한 해시 코드를 생성하는 생성자 매개 변수를 선택할 수 있습니다. 이러한 매개 변수를 피하는 경향이있는 단위 테스트는이 질문의 정신을 충족시킬 것입니다. 여기서 지름길은 소스 코드를 검사하고 equals(), 열심히 생각하고,이를 기반으로 테스트를 작성하는 것이지만, 경우에 따라 필요할 수 있지만 일반적인 문제를 포착하는 일반적인 테스트도있을 수 있으며 이러한 테스트도 정신을 충족시킵니다. 이 질문의.

클래스 테스트 할 경우 예를 들어, (데이터를) 호출하는 문자열로 구성된 문자열 소요 생성자 및 인스턴스가 equals()있었다 인스턴스를 굴복 equals()좋은 테스트는 것 아마 테스트 후를 :

  • new Data("foo")
  • 다른 new Data("foo")

내 생각에 올바른 결과를 산출하는 new Data(new String("foo"))것보다 올바른 해시 코드를 산출 할 가능성이 더 높지만 문자열이 인턴되지 않도록하기 위해 해시 코드를 확인할 수도 있습니다 Data.equals().

Eli Courtwright의 대답은 equals사양 에 대한 지식을 기반으로 해시 알고리즘을 깨는 방법을 열심히 생각하는 예입니다 . 특수 컬렉션의 예는 사용자가 만든 Collections가 때때로 나타나고 해시 알고리즘에서 엉망이되는 경향 이 있으므로 좋은 컬렉션입니다 .


답변

이것은 테스트에서 여러 개의 어설 션이있는 유일한 경우 중 하나입니다. equals 메소드를 테스트해야하므로 동시에 hashCode 메소드도 확인해야합니다. 따라서 각 equals 메소드 테스트 케이스에서 hashCode 계약도 확인하십시오.

A one = new A(...);
A two = new A(...);
assertEquals("These should be equal", one, two);
int oneCode = one.hashCode();
assertEquals("HashCodes should be equal", oneCode, two.hashCode());
assertEquals("HashCode should not change", oneCode, one.hashCode());

물론 좋은 hashCode를 확인하는 것은 또 다른 연습입니다. 솔직히 저는 hashCode가 동일한 실행에서 변경되지 않았는지 확인하기 위해 이중 검사를 수행하지 않을 것입니다. 이러한 종류의 문제는 코드 검토에서 파악하여 개발자가 왜 이것이 좋은 방법이 아닌지 이해하도록 도와줌으로써 더 잘 처리됩니다. hashCode 메서드를 작성합니다.


답변

http://code.google.com/p/guava-libraries/source/browse/guava-testlib/src/com/google/common/testing/EqualsTester.java 와 유사한 것을 사용
하여 equals 및 hashCode를 테스트 할 수도 있습니다 .