2009-05-07 11 views
21

次のクラスは、equals/hashCode contractのジェネリックテスターとして機能します。これは家庭試験の枠組みの一部です。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のリンクを再利用していますので、クレジットを得てください)。PointとColorPointクラスに関するTransitivityのセクションをご覧ください。

実際、実装ではサブクラスのインスタンスを含むテストを書くことができませんが、ObjectTestは汎用クラスであるため、すべてのデータポイントが単一のクラス(テストされるクラス)。型パラメーターを完全に削除するほうが良いかもしれません。思考のためだけの食糧。

+0

実際に!ありがとう、私はタイプパラメータT. – dfa

5

ジョシュア・ブロックは、ハッシュコードの契約をレイアウトし、chapter 3 of "Effective Java"に等しいです。あなたはそれの大部分をカバーしたように見えます。私が何かを逃したかどうかを確認するために、文書をチェックしてください

+1

また、Objectのjavadocは非常に詳細です – dfa

0

多分私は何かが不足しているかもしれませんが、equalsIsSymmetricテストは、実際には同じ値を持つDataPoints(例:String a = "a"; String a2 = "a";)が必要な場合のみ正しくテストされます このテストは、2つのパラメータが1つのインスタンス(つまりequalsIsSymmetric(a、a);)である場合にのみ実行されます。実際には、equalsが対称要件の代わりに 'reflection'要件に従うかどうか再度テストします。

+0

を除外しています。このため、テストには 'assumeThat(y.equals(x)、is(true))' – dfa

+0

はいですが、現在の設定では、 x 'とx!= yとx.equals(y)を保持する' y 'があります。その場合、notEqualsWorksテストは失敗します。したがってequalsIsSymmetricテストは、xとyについてのみ実行されます(x == yの場合)。 –

+0

ええ。上記の設定を前提として、JUnitはequalsIsSymmetric(a、a)とequalsIsSymmetric(b、b)を実行します。右? – dfa

0

notEqualsWorks(Object x、Object y)の理論は、falseです.2つの異なるインスタンスは、equalsメソッドによって論理的に等しくなることがあります。インスタンスが異なる場合はインスタンスが論理的に異なっていると仮定しています。

上記の独自の例を使用して、以下の二つの異なるデータ点は、(!= 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)は、テスト全体がこのようなオブジェクトを持つことに関するものであっても同等のデータポイントを持つ他の理論を実行できないようにする必要があると考えています。

このようなデータポイントがなければ、反射性のみがテストされます。