如何断言Iterable包含具有特定属性的元素?

时间:2012-08-28 19:43:20

标签: java unit-testing junit4 hamcrest

假设我想使用此签名对方法进行单元测试:

List<MyItem> getMyItems();

假设MyItem是一个包含许多属性的Pojo,其中一个属性为"name",可通过getName()访问。

我关心验证的是List<MyItem>或任何Iterable包含两个MyItem个实例,其"name"个属性的值为"foo""bar"。如果任何其他属性不匹配,我真的不关心此测试的目的。如果名称匹配,则表示测试成功。

如果可能,我希望它是单行的。这是我想要做的事情的一些“伪语法”。

assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});

Hamcrest对这类事情有好处吗?如果是这样,我上面的伪语法的hamcrest版本究竟是什么?

8 个答案:

答案 0 :(得分:106)

谢谢@Razvan,他指出了我正确的方向。我能够在一条线上获得它并且我成功地追捕了Hamcrest 1.3的进口。

进口:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;

代码:

assertThat( myClass.getMyItems(), contains(
    hasProperty("name", is("foo")), 
    hasProperty("name", is("bar"))
));

答案 1 :(得分:46)

尝试:

assertThat(myClass.getMyItems(),
                          hasItem(hasProperty("YourProperty", is("YourValue"))));

答案 2 :(得分:38)

它不是特别的Hamcrest,但我认为值得一提。我在Java8中经常使用的是:

assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));

(编辑Rodrigo Manyari的轻微改进。它的冗长一点。请参阅评论。)

阅读可能有点困难,但我喜欢类型和重构安全性。 它对于组合测试多个bean属性也很酷。例如与类似Java的&amp;&amp;过滤器lambda中的表达式。

答案 3 :(得分:18)

Assertj擅长这一点。

import numpy as np
import pandas as pd
from xlsxwriter.utility import xl_range

writer = pd.ExcelWriter('test.xlsx', engine='xlsxwriter')
workbook = writer.book

df = pd.DataFrame({'Data': np.random.rand(255)})
df.to_excel(writer, 'TEST')
test_sheet = writer.sheets['TEST']

chart = workbook.add_chart({'type': 'scatter'})
chart.add_series(
    {
        'name': 'Rand',
        'marker': {
            'type': 'circle',
            'size': 2,
            'fill': {'color': '#008000'},
            'border': {'none': True},
        },
        'line': {'none': True},
        'categories': "=TEST!{}".format(xl_range(1, 0, 255, 0)),
        'values': "=TEST!{}".format(xl_range(1, 1, 255, 1))
    }
)
test_sheet.insert_chart('C1', chart)        
writer.close()

与hamcrest相比,assertj的大优点是易于使用代码完成。

答案 4 :(得分:7)

AssertJ在UserStatus中提供了一项出色的功能:您可以传递POCO来提取字段。它在编译时提供检查。
您也可以先声明大小。

它将给出:

extracting()

Function断言,无论顺序如何,列表仅包含这些值。

要断言该列表包含这些值(无论顺序如何,但可能还包含其他值),请使用import static org.assertj.core.api.Assertions; Assertions.assertThat(myClass.getMyItems()) .hasSize(2) .extracting(MyItem::getName) .containsExactlyInAnyOrder("foo", "bar");

containsExactlyInAnyOrder()

答案 5 :(得分:4)

只要你的List是一个具体的类,只要你在MyItem上实现了equals()方法,就可以简单地调用contains()方法。

// given 
// some input ... you to complete

// when
List<MyItems> results = service.getMyItems();

// then
assertTrue(results.contains(new MyItem("foo")));
assertTrue(results.contains(new MyItem("bar")));

假设您已经实现了一个接受您想要断言的值的构造函数。我意识到这不是一行,但知道缺少哪个值而不是一次检查两个值是有用的。

答案 6 :(得分:0)

AssertJ 3.9.1支持anyMatch方法中的直接谓词用法。

assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())

这通常适用于任意复杂的情况。

对于简单的情况,我更喜欢使用extracting方法(请参见上文),因为生成的可迭代的被测对象可能支持具有更好可读性的值验证。 示例:它可以在Frank Neblung的答案中提供专门的API,例如contains方法。或者您也可以稍后再调用anyMatch并使用诸如"searchedvalue"::equals之类的方法引用。也可以将多个提取器放入extracting方法中,然后使用tuple()验证结果。

答案 7 :(得分:0)

除了hasProperty以外,您还可以尝试使用具有提取功能的hamcrest-more-matchers where匹配器。就您而言,它看起来像:

import static com.github.seregamorph.hamcrest.MoreMatchers.where;

assertThat(myClass.getMyItems(), contains(
    where(MyItem::getName, is("foo")), 
    where(MyItem::getName, is("bar"))
));

这种方法的优点是:

  • 并非总是可以通过字段来验证值是否使用get-method计算
  • 如果不匹配,则应该显示一条带有诊断信息的失败消息(请注意已解析的方法参考 MyItem.getName
Expected: iterable containing [Object that matches is "foo" after call
MyItem.getName, Object that matches is "bar" after call MyItem.getName]
     but: item 0: was "wrong-name"
  • 它适用于Java 8,Java 11和Java 14