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

Kev*_*uli 99 java unit-testing hamcrest junit4

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

List<MyItem> getMyItems();
Run Code Online (Sandbox Code Playgroud)

假设MyItem是一个Pojo,它有许多属性,其中一个是"name"通过访问的getName().

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

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

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

哈姆克雷斯特会对这类事情有好处吗?如果是这样,我上面的伪语法的hamcrest版本到底是什么?

Kev*_*uli 116

谢谢@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;
Run Code Online (Sandbox Code Playgroud)

代码:

assertThat( myClass.getMyItems(), contains(
    hasProperty("name", is("foo")), 
    hasProperty("name", is("bar"))
));
Run Code Online (Sandbox Code Playgroud)

  • 如果您不知道项目的顺序,请使用“containsInAnyOrder”(来自同一父类):) (5认同)

Raz*_*van 49

尝试:

assertThat(myClass.getMyItems(),
                          hasItem(hasProperty("YourProperty", is("YourValue"))));
Run Code Online (Sandbox Code Playgroud)

  • 就像一个副节点 - 这是一个hamcrest解决方案(不是断言) (2认同)

Mar*_*Eis 40

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

assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));
Run Code Online (Sandbox Code Playgroud)

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

它可能有点难以阅读,但我喜欢类型和重构安全性.它对于组合测试多个bean属性也很酷.例如,在过滤器lambda中使用类似java的&&表达式.

  • 稍微改进:assertTrue(myClass.getMyItems().stream().anyMatch(item - >"foo".equals(item.getName())); (2认同)
  • 此解决方案浪费了显示适当错误消息的可能性。 (2认同)
  • 我的意思是,如果您对布尔值进行断言,您将失去自动打印实际/预期差异的能力。可以使用匹配器进行断言,但您需要将此响应修改为与本页面中的其他响应类似才能执行此操作。 (2认同)

Fra*_*ung 20

Assertj擅长这一点.

import static org.assertj.core.api.Assertions.assertThat;

    assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");
Run Code Online (Sandbox Code Playgroud)

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


dav*_*xxx 10

AssertJ提供了一个很好的功能extracting():你可以传递Functions来提取字段.它在编译时提供检查.
您也可以轻松地断言尺寸.

它会给:

import static org.assertj.core.api.Assertions;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName)
          .containsExactlyInAnyOrder("foo", "bar"); 
Run Code Online (Sandbox Code Playgroud)

containsExactlyInAnyOrder() 断言该列表仅包含这些值,无论顺序如何.

要声明列表包含这些值,无论顺序如何,但也可能包含其他值,请使用contains():

.contains("foo", "bar"); 
Run Code Online (Sandbox Code Playgroud)

  • 不明白为什么这没有赞成.我想,到目前为止,这是最好的答案. (4认同)
  • AssertJ 库比 JUnit 断言 API 更具可读性。 (2认同)

Bra*_*rad 5

只要您的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")));
Run Code Online (Sandbox Code Playgroud)

假设您实现了一个构造函数,该构造函数接受要声明的值。我意识到这不是一行,但是知道丢失哪个值而不是一次检查两个值很有用。


Tom*_*ský 5

AssertJ 3.9.1 支持在方法中直接使用谓词anyMatch

assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())
Run Code Online (Sandbox Code Playgroud)

这通常适合任意复杂条件的用例。

对于简单的条件,我更喜欢使用extracting方法(见上文),因为结果可迭代的测试可能支持具有更好可读性的值验证。例如:它可以提供专门的API,例如containsFrank Neblung答案中的方法。或者您可以anyMatch稍后调用它并使用方法引用,例如"searchedvalue"::equals. 也可以将多个提取器放入extracting方法中,随后使用 验证结果tuple()