用Java维护对象方法契约的自动unit testing?

在开发Java应用程序时,我经常重写Object方法(通常是equals和hashCode)。 我想通过某种方式系统地检查我是否遵守每个类的Object方法的合同。 例如,我想要测试断言对于相等的对象,哈希码也是相等的。 我正在使用JUnit测试框架,所以最好我想要一些JUnit解决方案,我可以自动生成这些测试,或者一些测试用例可以以某种方式访问​​我的所有类并确保合同得到维护。

我正在使用JDK6和JUnit 4.4。

     public static void checkObjectIdentity(Object a1,Object a2,Object b1){
         assertEquals(a1,a2);
         assertEquals(a2,a1);
         assertNotSame(a1,a2);
         assertEquals(a1.hashCode(),a2.hashCode());
         assertFalse(a1.equals(B1));
         assertFalse(a2.equals(B1));
         assertFalse(b1.equals(A1));
         assertFalse(b1.equals(A2));
     }

用法:

         checkObjectIdentity(new Integer(3),new Integer(3),new Integer(4));

想不出更好的事情。 找到错误时添加对checkObjectIdentity的新调用。

只是对这个问题的一些初步想法(这可以解释为什么一整个小时后仍然没有答案!?;)

在实现问题的解决方案时,似乎有两个部分:

1 /检索我自己的每个类。 很简单,你给一个jar名称,Junit测试初始化​​方法会:

  • 检查该jar是否在JUnit执行类路径中
  • 读取并加载其中的每个类
  • 仅记忆已声明和重新定义equals()和hash()的那些(通过Reflection)

2 /测试每个物体
…其中有一个问题:你必须实例化那些对象,即创建两个实例,并将它们用于equals()测试。

这意味着如果你的构造函数被认为是参数,你必须考虑,

  • 原始类型参数(int,boolean,float,…)或String,限制值的每个组合(对于String,“xxx”,“”,null; fonr int,0,-x,+ x,-Integer .MIN,+ Integer.MAX,…等等)
  • 对于非基本类型,构建要传递给要测试的对象的构造函数的实例(意味着你递归地必须考虑该参数的构造函数参数:原始类型与否)

最后,并非每个为这些构造函数自动创建的参数都会以函数方式有意义,这意味着其中一些值将无法构建实例,因为Assert:必须检测到。

然而,它似乎是可能的(如果你愿意,你可以让它成为代码挑战 ),但我想首先让其他StackOverflow读者回答这个问题,因为他们可能会看到一个更简单的解决方案。


为了避免组合问题并使测试相关测试值保持接近实际代码本身,我建议定义专用注释,其中String表示构造函数的有效值。 将位于您的一个对象的equals()重写方法的正上方。

然后将读取这些注释值,并将组合这些注释的实例组合起来以测试equals()。 这将使组合数量足够下降

Side-node:一个通用的JUnit测试用例当然要检查,对于每个equals()测试,有:

  • 一些注释如上所述(除非只有默认构造函数可用)
  • 一个相应的hash()方法也被覆盖(如果没有,如果会抛出一个断言exception并在该类上失败)

我认为VonC在正确的轨道上,但我甚至会选择不太复杂的东西,例如参数化测试,它接受.class对象(正在测试Object方法),然后是可变数量的构造函数args。 然后,您必须使用reflection来查找与传入参数的类型匹配的构造函数,并调用构造函数。 此测试将假定传递给它的参数将创建对象的有效实例。

这个解决方案的缺点是你必须“注册”你想用这个测试类测试的每个类,你必须确保给构造函数提供了有效的输入,这并不总是那么容易。 从这个角度来看,无论如何,我都在考虑这是否比为每个class级手动编写所有测试更多或更少。

如果您认为这可行,请投票…如果您希望我更多地将其清除(如果事实certificate这是一个可行的解决方案,我可能会这样做)

除非您对类强加约束,否则此问题没有“简单”的解决方案。

例如,如果您为给定的类使用了几个构造函数,那么如何确保在equals / hash方法中充分考虑所有参数? 默认值怎么样? 遗憾的是,这些都是盲目无法实现的。

[社区post在这里,没有涉及业力;)]

这是您的另一个代码挑战

一个 java类,实现一个JUnit测试用例,主要方法能够自己启动JUnit!

这堂课还将:

  • override hash()和equals()
  • 定义一些属性(使用基本类型)
  • 定义默认构造函数,但也有一些具有各种参数组合的构造函数
  • 定义一个能够枚举“有趣”值以传递给那些构造函数的注释
  • 使用那些“有趣”值注释equals()

测试方法采用类名参数(此处:它将是自身),检查具有该名称的类是否具有带有“有趣值”注释的equals()重写方法。
如果是,它将根据注释构建适当的实例(自身),并测试equals()

这是一个独立的测试类,它定义了一种机制,可以推广到任何具有带注释的重写equals()函数的类。

请使用JDK6和JUnit4.4

应该将这个类复制粘贴到一个空的java项目的相应包中…然后运行;)


为了回应尼古拉斯,增加了一些想法(见评论):

  • 是的,测试所需的数据在要测试的候选类别内(即,一个覆盖等于并帮助任何’自动测试器’构建适当的实例)
  • 我并不认为这与“测试逻辑” 完全相同 ,而是作为对应该做什么等于的有用的评论(顺便说一句,作为上述测试人员要利用的数据;))

表示潜在测试数据的注释是否永远不会出现在类本身中?…嘿,这可能是一个很好的问题要问:)

也许我误解了这个问题(而且太过于CS),但听起来你所描述的问题在一般情况下是可判定的。

换句话说,unit testing的唯一方法可以确保覆盖方法在所有输入上的工作方式相同,因为重写的方法是在所有输入上尝试它; 在等于的情况下,这将意味着所有对象状态。

我不确定任何当前的测试框架是否会自动减少并为您提取可能性。

我有一个第一个粗略的实现,在这里只使用原始参数与Constructor进行equals测试。 只需将其复制粘贴到test.MyClass.java文件中并运行即可。

警告:1720行代码(在findbugs中为0个错误,在“已修改”的checkstyle中为0,对于所有函数,圈数复杂度为10以下)。

查看以下所有代码: 通过注释在java类中自动测试equals函数

一个旧问题的新答案,但是在2011年5月, Guava (以前的Google Collections)发布了一个类,它删除了许多名为EqualsTester的样板。 您仍然需要创建自己的实例,但它需要将每个对象与自身进行比较,将null,与相等组中的每个对象,每个其他相等组中的每个对象以及应该不匹配的秘密实例进行比较。 它还检查a.equals(b)意味着所有这些组合中的a.hashCode() == b.hashCode()

Javadoc的示例:

 new EqualsTester() .addEqualityGroup("hello", "h" + "ello") .addEqualityGroup("world", "wor" + "ld") .addEqualityGroup(2, 1 + 1) .testEquals();