用于hashCode / equals契约的JUnit理论

时间:2009-05-07 23:03:06

标签: java unit-testing junit

以下类用作equals / hashCode契约的通用测试器。它是本土测试框架的一部分。

  • 你怎么看?
  • 我如何(强)测试这门课程?
  • 很好用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 = "";
}

5 个答案:

答案 0 :(得分:8)

要考虑的一件事:测试对象与equals合同的一致性应该涉及其他类型的实例。特别是,子类或超类的实例可能会出现问题。约书亚布洛赫对Effective Java中的相关陷阱给出了很好的解释(我正在重复使用duffymo的链接,所以他应该对此有所了解) - 请参阅涉及Point和ColorPoint类的Transitivity下的部分。

是的,您的实现不会阻止某人编写涉及子类实例的测试,但由于ObjectTest是一个泛型类,因此它给人的印象是所有数据点都应来自单个类(正在测试的课程)。最好完全删除类型参数。只是值得深思。

答案 1 :(得分:5)

Joshua Bloch在chapter 3 of "Effective Java"中列出了哈希码和等于的合同。看起来你已经涵盖了很多。检查文件,看看我是否遗漏了任何东西。

答案 2 :(得分:0)

也许我错过了什么,但是如果你必须使用具有相同值的DataPoints,则equalsIsSymmetric测试实际上只是正确测试(例如String a =“a”; String a2 =“a”;) 否则,仅当2个参数是一个实例(即equalsIsSymmetric(a,a);)时才进行该测试。实际上,如果等于遵守“反射”要求而不是对称要求,则再次测试。

答案 3 :(得分:0)

notEqualsWorks(对象x,对象y)理论是错误的:根据它们的equals方法,两个不同的实例在逻辑上可能仍然相等;你假设实例在逻辑上是不同的,如果它们是不同的引用。

使用上面的例子,下面两个不同的数据点(a!= a2)仍然相同,但未通过notEqualsWorks测试:

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

答案 4 :(得分:0)

equalsWorks(Object x, Object y)方法与equalsIsReflexive(Object x)进行的测试完全相同。它应该删除。

我还认为应该删除notEqualsWorks(Object x, Object y),因为即使整个测试都是关于拥有这样的对象,它也会阻止人们使用相同的数据点进行其他理论。

如果没有这些数据点,反身性是唯一经过测试的。