【问题标题】:Hamcrest - Elegant way to test complex object with samepropertyvaluesasHamcrest - 用相同的属性值测试复杂对象的优雅方法
【发布时间】:2016-02-26 10:47:19
【问题描述】:

我有相当复杂的对象结构(带有一堆原始字段和对象引用)并且想要测试除少数字段之外的所有字段。举个例子;

ComplexObject actual = generateMagically("someInput");
ComplexObject expected = ActualFunction.instance.workMagically(actual);

// we want to be sure that workMagically() would create a new ComplexObject
// with some fields are different than "actual" object.

// assertThat(actual, samePropertyValuesAs(expected)); would check all fields.
// what I want is actually; - notice that "fieldName1" and "fieldName2" are 
// primitives belong to ComplexObject
assertThat(actual, samePropertyValuesExceptAs(expected, "fieldName1", "fieldName2"))

由于我不想手动检查所有字段,我相信必须有一种方法可以优雅地编写该测试。有什么想法吗?

干杯。

【问题讨论】:

  • 那么你有两个bag对象,想要进行深度比较?
  • 不确定是否称它们为包对象,有许多原始字段和其他对象引用。我想跳过的字段是属于 ComplexObject 的原始字段。我们可以说这将是一个深入的比较。
  • 您是否只想将 samePropertyValuesExceptAs 的 Matcher 实现作为此问题的答案?如果是这样,那么您可以创建org.hamcrest.beans.SamePropertyValuesAs<T> 的副本,并向其中添加另一个构造函数/静态工厂方法,该方法将删除排除的属性,使其不再被测试。
  • 作为注释,你可以试试assertj:它有一个更流畅的api,IDE完成,你可以写assertThat(actual).isEqualToIgnoringGivenFields(expected, "fieldName1", "fieldName2);joel-costigliola.github.io/assertj/index.html
  • @JérémieB,我建议您将您的评论作为答案,因为这就是我要寻找的问题。谢谢!

标签: java junit hamcrest


【解决方案1】:

您应该看看 shazamcrest,这是一个很棒的 Hamcrest 扩展程序,可以满足您的需求。

assertThat(expected, sameBeanAs(expectedPerson).ignoring("fieldName1").ignoring("fieldName2"));

https://github.com/shazam/shazamcrest#ignoring-fields

【讨论】:

  • 如果有人想知道sameBeanAs 在哪里,它在com.shazam.shazamcrest.matcher.Matchers 类中。
【解决方案2】:

只需将要忽略的属性列表作为第二个参数传递给 samePropertyValuesAs。

Hamcrest 匹配器 API

public static <B> Matcher<B> samePropertyValuesAs(B expectedBean, String... ignoredProperties)

例如

samePropertyValuesAs(salesRecord,"id")

【讨论】:

    【解决方案3】:

    如果ComplexObject可以自己修改,一般我看到两种解决方案。

    您可以引入一个表示ComplexObject 属性的接口,这些属性正在被ActualFunction 更改。然后您可以测试该新接口的所有属性是否已更改。这将要求ComplexObject 实现该新接口。

    另一种方法是将ComplextObject 的被ActualFunction 更改的属性替换为包含所有这些属性的新类型的新属性。更好的设计是让ActualFunction 返回一个新类型的实例。

    【讨论】:

    • 感谢 SpaceTrucker。 (1) 我认为仅出于测试目的在域对象中实现接口并不是最佳实践。 (2) 对于另一种方法,您实际上提出添加一个具有更新属性的新属性(我假设是一个新对象)?再次更改对象只是为了测试,我猜不是。
    • @tugcem 这不仅仅是关于测试,而是为了让ActualFunction 的效果更加明显,它所代表的功能更具表现力。例如,如果ActualFunction 的实现将更改为同时修改另一个属性,那么如果使用我的第二种方法,效果将立即可见,因为新类型将获得该新属性。
    • 我实际上不需要为 workMagically() 函数添加额外的表达式或可见性来更改属性。事实上,我们不想对更新的字段说任何明确的内容,并希望此类的用户意识到这一点。
    【解决方案4】:

    上次我有类似的要求时,我得出的结论是,手动编写代码和测试来断言某些值已更新本质上是脆弱且容易出错的。

    我将包对象中的字段外部化,并在编译时为包类本身和复印机生成了 Java 源文件。这样您就可以测试实际代码(生成器)并将域的实际定义完全放在一个地方,因此复制代码不会过时。

    描述属性的语言可以是任何你喜欢的语言,从 JSON 模式到 XML 再到 Java 本身(Java 示例如下 - 自定义注释将从生成器中使用)

    public class MyBag {
      @Prop public int oh;
      @Prop public String yeah;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-27
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多