Java hashCode/equals契约的JUnit理论

Java hashCode/equals契约的JUnit理论,java,unit-testing,junit,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

下面的类用作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));
    }
}

约书亚·布洛赫(Joshua Bloch)列出了散列码的合同,并将其等同于。看起来你涵盖了很多。检查文档以查看是否遗漏了任何内容。

需要考虑的一件事是:测试对象是否符合equals契约应该涉及其他类型的实例。特别是,子类或超类的实例可能会出现问题。Joshua Bloch很好地解释了中的相关缺陷(我正在重用duffymo的链接,因此他应该为此获得好评)——请参见涉及Point和ColorPoint类的及物性部分


诚然,您的实现并不会阻止某人编写涉及子类实例的测试,但因为
ObjectTest
是一个泛型类,所以它给人的印象是所有数据点都应该来自单个类(被测试的类)。最好完全删除类型参数。值得思考。

也许我遗漏了一些东西,但事实上,只有在必须使用具有相同值的数据点(例如字符串a=“a”字符串a2=“a”)时,才能正确测试Equalsissymmetry测试 否则,该测试仅在两个参数为一个实例(即相等对称(a,a);)时进行。事实上,你再次测试等式是否遵循“反射”要求而不是对称要求。

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

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

@DataPoint
public static String a = "a";
@DataPoint
public static String a2 = new String("a");
equalworks(objectx,objecty)
方法正在进行与
equalisreflexive(objectx)
相同的测试。它应该被移除

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


如果没有这些数据点,自反性是唯一需要测试的东西。

另外,javadoc for Object非常详细。如果我读对了,equalsIsSymmetric方法中的最后一条语句不应该是assertThat,而不是AssumetThat吗?那么,您要的是一个自主开发的解决方案,但是您知道有哪些开源库可以进行这些常见的测试吗?(我还建议可比较和可序列化。)我对使用这样的框架感兴趣。没有这样的框架(如我所见)。我可以把这段代码贡献给一个开源项目(见下面弗兰克的回答)@ivo:我已经用dollar:的确!谢谢,我正在删除类型参数T。出于这个原因,测试假设(y.equals(x),is(true))是,但在当前设置中,它无法创建包含x的“x”和“y”y和x等于(y),因为在这种情况下notEqualsWorks测试将失败。所以等式对称检验只对x和y进行,其中x==y。假设上面的设置,JUnit将执行:equalsIsSymmetric(a,a)和equalsIsSymmetric(b,b)。是吗?没错,但你应该注意到这个理论有如下要求:“用户必须提供不相等的数据点”。
@DataPoint
public static String a = "a";
@DataPoint
public static String a2 = new String("a");