2009-05-07 5 views
21

다음 클래스는 equals/hashCode 계약의 일반 테스터 역할을합니다. 이것은 가정에서 테스트 한 프레임 워크의 일부입니다.hashCode/equals 계약에 대한 JUnit 이론

  • 당신은 어떻게 생각하십니까?
  • 이 클래스를 어떻게 테스트 할 수 있습니까?
  • Junit 이론을 잘 사용하고 있습니까?

클래스 :

@Ignore 
@RunWith(Theories.class) 
public abstract class ObjectTest { 

    // For any non-null reference value x, x.equals(x) should return true 
    @Theory 
    public void equalsIsReflexive(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assertThat(x.equals(x), is(true)); 
    } 

    // For any non-null reference values x and y, x.equals(y) 
    // should return true if and only if y.equals(x) returns true. 
    @Theory 
    public void equalsIsSymmetric(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(y, is(not(equalTo(null)))); 
     assumeThat(y.equals(x), is(true)); 
     assertThat(x.equals(y), is(true)); 
    } 

    // For any non-null reference values x, y, and z, if x.equals(y) 
    // returns true and y.equals(z) returns true, then x.equals(z) 
    // should return true. 
    @Theory 
    public void equalsIsTransitive(Object x, Object y, Object z) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(y, is(not(equalTo(null)))); 
     assumeThat(z, is(not(equalTo(null)))); 
     assumeThat(x.equals(y) && y.equals(z), is(true)); 
     assertThat(z.equals(x), is(true)); 
    } 

    // For any non-null reference values x and y, multiple invocations 
    // of x.equals(y) consistently return true or consistently return 
    // false, provided no information used in equals comparisons on 
    // the objects is modified. 
    @Theory 
    public void equalsIsConsistent(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     boolean alwaysTheSame = x.equals(y); 

     for (int i = 0; i < 30; i++) { 
      assertThat(x.equals(y), is(alwaysTheSame)); 
     } 
    } 

    // For any non-null reference value x, x.equals(null) should 
    // return false. 
    @Theory 
    public void equalsReturnFalseOnNull(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assertThat(x.equals(null), is(false)); 
    } 

    // Whenever it is invoked on the same object more than once 
    // the hashCode() method must consistently return the same 
    // integer. 
    @Theory 
    public void hashCodeIsSelfConsistent(Object x) { 
     assumeThat(x, is(not(equalTo(null)))); 
     int alwaysTheSame = x.hashCode(); 

     for (int i = 0; i < 30; i++) { 
      assertThat(x.hashCode(), is(alwaysTheSame)); 
     } 
    } 

    // If two objects are equal according to the equals(Object) method, 
    // then calling the hashCode method on each of the two objects 
    // must produce the same integer result. 
    @Theory 
    public void hashCodeIsConsistentWithEquals(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x.equals(y), is(true)); 
     assertThat(x.hashCode(), is(equalTo(y.hashCode()))); 
    } 

    // Test that x.equals(y) where x and y are the same datapoint 
    // instance works. User must provide datapoints that are not equal. 
    @Theory 
    public void equalsWorks(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x == y, is(true)); 
     assertThat(x.equals(y), is(true)); 
    } 

    // Test that x.equals(y) where x and y are the same datapoint instance 
    // works. User must provide datapoints that are not equal. 
    @Theory 
    public void notEqualsWorks(Object x, Object y) { 
     assumeThat(x, is(not(equalTo(null)))); 
     assumeThat(x != y, is(true)); 
     assertThat(x.equals(y), is(false)); 
    } 
} 

사용 :

import org.junit.experimental.theories.DataPoint; 

public class ObjectTestTest extends ObjectTest { 

    @DataPoint 
    public static String a = "a"; 
    @DataPoint 
    public static String b = "b"; 
    @DataPoint 
    public static String nullString = null; 
    @DataPoint 
    public static String emptyString = ""; 
} 
+0

정확하게 읽는다면 equalsIsSymmetric 메서드의 마지막 문장을 어설트해서는 안됩니다. –

+0

네, 고마워요. :) – dfa

+0

자택 개발자 용 솔루션을 찾으러 가고 있지만 이런 종류의 공통 테스트를 수행하기 위해 오픈 소스 라이브러리에 대해 알고 있습니까?(필자도 비교 가능하고 직렬화 가능하다고 제안합니다.) 그러한 프레임 워크를 사용하는 데 관심이 있습니다. – ivo

답변

8

고려해야 할 사항 : equals 계약에 대한 오브젝트 적합성 테스트는 다른 유형의 인스턴스를 포함해야합니다. 특히, 서브 클래스 나 수퍼 클래스의 인스턴스에 문제가 발생할 가능성이 있습니다. Joshua Bloch는 Effective Java에 관련된 함정에 대한 훌륭한 설명을주었습니다. (나는 duffymo의 링크를 재사용하고 있기 때문에 신용을 얻어야합니다.) 포인트 및 ColorPoint 클래스와 관련된 Transitivity 섹션을 참조하십시오.

사실, 구현하면 누군가가 하위 클래스의 인스턴스를 포함하는 테스트를 작성하지 못하지만 ObjectTest은 일반 클래스이므로 모든 데이터 포인트가 단일 클래스 (테스트중인 클래스). type 매개 변수를 모두 제거하는 것이 좋습니다. 생각을위한 음식.

+0

실제로! 고마워, 나는 테스트가'assumThat (y.equals (x), is (true))' – dfa

5

여호수아 블로흐는 해시 코드에 대한 계약을 낳는 chapter 3 of "Effective Java"에 같습니다. 네가 그걸 다뤘던 것 같아. 내가 무엇을 놓쳤는 지 확인하기 위해 문서를 확인하십시오.

+1

또한 javadoc for Object는 매우 상세합니다 – dfa

0

어쩌면 내가 누락되었지만 같은 값 (예 : String a = "a"; String a2 = "a";)을 가진 DataPoints가 있어야 equalsIsSymmetric 테스트가 실제로 올바르게 테스트됩니다. 그렇지 않으면 이 테스트는 2 개의 매개 변수가 하나의 인스턴스 (예 : equalsIsSymmetric (a, a);) 일 때만 수행됩니다. 실제로 equals가 대칭 요구 사항 대신 '반사'요구 사항을 따르는 경우 다시 테스트합니다.

+0

을 제거하지만 현재 설정에서는 ' x '와'y '는 x! = y와 x.equals (y)를 포함하고 있습니다. 왜냐하면 notEqualsWorks 테스트가 실패 할 것이기 때문입니다. 따라서 equalsIsSymmetric 테스트는 x와 y에 대해서만 수행됩니다. 여기서 x == y입니다. – dfa

+0

yes라는 ​​형식 매개 변수 T. –

+0

예. 위의 설정을 가정하면 JUnit은 equalsIsSymmetric (a, a) 및 equalsIsSymmetric (b, b)를 실행합니다. 권리? – dfa

0

notEqualsWorks (Object x, Object y) 이론은 false입니다. 두 개의 개별 인스턴스가 equals 메서드에 따라 논리적으로 동일 할 수 있습니다. 서로 다른 참조 인 경우 인스턴스가 논리적으로 다르다고 가정합니다.

상기 고유 예를 사용하면, 아래의 두 가지 데이터 포인트는 (! A = A2) 그럼에도 불구하고 동등하지만 notEqualsWorks 테스트 실패 다음 equalsWorks(Object x, Object y) 방법 equalsIsReflexive(Object x)로 매우 동일한 시험을하고

@DataPoint 
public static String a = "a"; 
@DataPoint 
public static String a2 = new String("a"); 
+0

사실이지만 이론에는 다음과 같은 요구 사항이 있습니다. "사용자가 같지 않은 데이터 포인트를 제공해야합니다". – dfa

0

. 제거해야합니다.

나는 또한 notEqualsWorks(Object x, Object y)이 제거되어야한다고 생각합니다. 왜냐하면 전체 테스팅이 그러한 객체를 가지고 있다고 생각한다고해도 데이터 포인트가 다른 이론을 수행하지 못하게하기 때문입니다.

이러한 데이터 요소가 없으면 반사율 만이 테스트 된 것입니다.