【问题标题】:Assert collection contains object of custom class, which does not override equals/hashcode断言集合包含自定义类的对象,它不会覆盖 equals/hashcode
【发布时间】:2015-09-21 15:00:53
【问题描述】:

我们有一个包含多个字段的自定义类,出于业务领域的原因,我们无法覆盖equals/hashcode方法

尽管如此,在单元测试期间,我们应该断言集合是否包含此类的项

List<CustomClass> customObjectList = classUnderTest.methodUnderTest();
//create customObject with fields set to the very same values as one of the elements in customObjectList
//we should assert here that customObjectList contains customObject

但是,到目前为止,我们还没有找到任何不覆盖 equals/hashcode 的解决方案,例如汉克雷斯特

assertThat(customObjectList, contains(customObject));

导致 AssertionError 引用

Expected: iterable containing [<CustomClass@578486a3>]
but: item 0: was <CustomClass@551aa95a>

是否有无需逐个字段比较的解决方案?

【问题讨论】:

    标签: java collections junit equals hamcrest


    【解决方案1】:

    如果您使用的是 Java8,您可以使用 Stream#anyMatch 和您自己的 customEquals 方法。像这样的东西会起作用 -

       assertTrue(customObjectList.stream()
                     .anyMatch(object -> customEquals(object,customObject)));
    

    更新以反映 Holger 的评论

    【讨论】:

    • anyMatch 需要谓词。修复使 filter 过时:customObjectList.stream().anyMatch(object -&gt; customEquals(object,customObject))
    • 你是对的(谢谢!),精神上混合并匹配 anyMatch 和 findFirst。相应更新。 FindFirst 下面,但我认为 anyMatch 更好。 assertTrue(customObjectList.stream() .filter(object -> customEquals(object,customObject)) .findFirst().isPresent());
    【解决方案2】:

    我想说谢谢你的所有回复,已经提出了一些非常好的观点

    但是,我在我的问题中忘记提到的是,我们的自定义类是递归的,即包含其他自定义类类型的字段,对于这些字段,相同的限制适用于 equals 和 hashcode 覆盖。不幸的是,提到的开箱即用解决方案(AssertJ、Nitor Creations)似乎都不支持深度比较

    尽管如此,似乎仍然有一个解决方案,那就是 Unitils 的 ReflectionAssert 类。以下似乎按我们的预期工作,甚至可以忽略集合中的元素顺序

    assertReflectionEquals(Arrays.asList(customObject1, customObject3, customObject2), customObjectList, ReflectionComparatorMode.LENIENT_ORDER);
    

    【讨论】:

      【解决方案3】:

      assertj 擅长这个。尤其是它的custom comparison strategy

      private static class CustomClass {
          private final String string;
      
          CustomClass(String string) {
              this.string = string;
          }
      
          // no equals, no hashCode!
      }
      
      @Test
      public void assertjToTheRescue() {
          List<CustomClass> list = Arrays.asList(new CustomClass("abc"));
      
          assertThat(list).usingFieldByFieldElementComparator().contains(new CustomClass("abc"));
      }
      

      assertj 提供了许多其他的usingComparator 方法。

      【讨论】:

        【解决方案4】:

        Fest Assertions 有以下内容:

        assertThat(expected).isEqualsToByComparingFields(actual);
        

        我认为它会在后台进行反射比较。我们遇到了类似的问题,这使我们免于编写自定义比较逻辑。

        另一件事是扩展您选择的断言框架,为您的确切类和案例提供一些东西。这种方法应该可以减少使用深度反射比较的一些性能开销。

        【讨论】:

        • 我在使用的 fest 版本中找不到这种方法。你用的是什么版本的Danail?我有festAssert: 'org.easytesting:fest-assert-core:2.0M10'
        • @anon58192932 我认为它应该在那里。至少我可以在文档中找到:javadox.com/org.easytesting/fest-assert-core/2.0M10/org/fest/…
        • 也许我的导入已关闭,而 IntelliJ 的自动完成功能并没有像我想象的那样帮助我找到它们。我很快就会在这里再试一试。但是我们可以假设assertThat(x).isEqualTo(y) 默认不使用深度比较吗?
        【解决方案5】:

        我知道您使用 Hamcrest 时遇到的问题有两种解决方案。第一个测试项目的一些属性。

        assertThat(customObjectList, contains(allOf(
            hasProperty("propertyA", equalTo("someValue")),
            hasProperty("propertyB", equalTo("anotherValue")))));
        

        或者您可以使用来自Nitor CreationsreflectEquals 匹配器:

        assertThat(customObjectList, contains(reflectEquals(customObject)));
        

        【讨论】:

          【解决方案6】:

          没有。

          Collection 接口的很多方法都是根据equals() 专门定义的。例如。 Collection#contains():

          如果此集合包含指定元素,则返回 true。更多的 正式地,返回 true 当且仅当此集合包含 at 至少一个元素e 使得(o==null ? e==null : o.equals(e))

          仅针对每个集合并逐个字段进行比较。您可以将比较/相等逻辑存储到静态实用程序类中,例如 CustomClasses 或类似的,然后编写自定义 Hamcrest 匹配器。

          或者,使用反射等号,例如从Apache Commons Lang,一个Hamcrest extension,或者(最好)迁移到AssertJ,它是has this functionality out of the box

          assertThat(ImmutableList.of(frodo))
                  .usingFieldByFieldElementComparator()
                  .contains(frodoClone);
          

          它既老套又狡猾,但足以进行测试。请不要在生产代码中使用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-11
            • 1970-01-01
            • 1970-01-01
            • 2015-07-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多