用于hashCode / equals契约的JUnit理论

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

  • 你有什么想法?
  • 我怎样(强)测试这门课程?
  • 这是Junit理论的一个很好的用途吗?

class上:

@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 = ""; } 

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

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

Joshua Bloch在“Effective Java”的第3章中列出了哈希码和等号的合同。 看起来你已经涵盖了很多。 检查文档,看看我是否遗漏了任何东西。

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

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

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

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

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

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

没有这样的数据点,reflection性是唯一被测试的东西。