使用 assertJ 断言列表元素的属性

Cor*_*onA 7 java junit assertj

我有一个有效的 hamcrest 断言:

assertThat(mylist, contains(
  containsString("15"), 
  containsString("217")));
Run Code Online (Sandbox Code Playgroud)

预期的行为是:

  • mylist == asList("Abcd15", "217aB") => 成功
  • myList == asList("Abcd15", "218") => 失败

如何将此表达式迁移到 assertJ。当然,存在一些幼稚的解决方案,例如断言第一个和第二个值,如下所示:

assertThat(mylist.get(0)).contains("15");
assertThat(mylist.get(1)).contains("217");
Run Code Online (Sandbox Code Playgroud)

但这些是对列表元素的断言,而不是对列表的断言。在列表上尝试断言将我限制为非常通用的函数。所以也许它只能通过自定义断言来解决,如下所示就可以了:

assertThat(mylist).elements()
  .next().contains("15")
  .next().contains("217")
Run Code Online (Sandbox Code Playgroud)

但是在我编写自定义断言之前,我会对其他人如何解决这个问题感兴趣?

编辑:一个额外的非功能性要求是,测试应该可以通过额外的约束轻松扩展。在 Hamcrest 中很容易表达额外的约束,例如

assertThat(mylist, contains(
  emptyString(),                                     //additional element
  allOf(containsString("08"), containsString("15")), //extended constraint
  containsString("217")));                           // unchanged
Run Code Online (Sandbox Code Playgroud)

对于此示例,依赖于列表索引的测试必须重新编号,使用自定义条件的测试必须重写完整条件(请注意,中的约束allOf不限于子字符串检查)。

Syn*_*oli 14

AssertJ v3.19.0 或更高版本:使用satisfiesExactly.

\n

2021 年发布的 AssertJ v3.19.0 添加了一个satisfiesExactly方法。

\n

所以你可以写:

\n
assertThat(mylist)\n    .satisfiesExactly(item1 -> assertThat(item1).contains("15"),\n                      item2 -> assertThat(item2).contains("217"));\n
Run Code Online (Sandbox Code Playgroud)\n

如果需要,您可以向各个元素添加更多断言:

\n
assertThat(mylist)\n    .satisfiesExactly(item1 -> assertThat(item1)\n                                      .contains("08")\n                                      .contains("15"),\n                      item2 -> assertThat(item2).contains("217"));\n
Run Code Online (Sandbox Code Playgroud)\n

与使用链的技术相比next(),该技术还为您检查列表大小。作为一个额外的好处,它允许您使用您喜欢的任何 lambda 参数,因此更容易阅读并跟踪您所在的元素。

\n


Joe*_*ola 7

对于这种断言,Hamcrest 优于 AssertJ,您可以使用条件模拟 Hamcrest,但您需要编写它们,因为 AssertJ 中没有提供开箱即用的功能(assertJ 的理念是不与 Hamcrest 在这方面竞争)。

在下一个 AssertJ 版本(即将发布!)中,您将能够重用 Hamcrest Matcher 来构建 AssertJ 条件,例如:

Condition<String> containing123 = new HamcrestCondition<>(containsString("123"));

// assertions succeed
assertThat("abc123").is(containing123);
assertThat("def456").isNot(containing123);
Run Code Online (Sandbox Code Playgroud)

最后一点,这个建议......

assertThat(mylist).elements()
                  .next().contains("15")
                  .next().contains("217")
Run Code Online (Sandbox Code Playgroud)

...不幸的是,由于泛型限制而无法工作,尽管您知道您有一个字符串列表,但 Java 泛型不够强大,无法StringAssert根据另一个 ( String)选择特定类型( ),这意味着您只能执行Object断言在元素上但不是String断言。

- 编辑 -

由于 3.13.0 可以asInstanceOf用来获取特定的类型断言,如果声明的类型是 Object 但运行时类型更具体,这很有用。

例子:

// Given a String declared as an Object
Object value = "Once upon a time in the west";

// With asInstanceOf, we switch to specific String assertion by specifying the InstanceOfAssertFactory for String
assertThat(value).asInstanceOf(InstanceOfAssertFactories.STRING)
                 .startsWith("Once");`
Run Code Online (Sandbox Code Playgroud)

https://assertj.github.io/doc/#assertj-core-3.13.0-asInstanceOf


JB *_*zet 1

我发现的最接近的是编写一个“ContainsSubstring”条件,以及一个静态方法来创建一个条件,然后使用

assertThat(list).has(containsSubstring("15", atIndex(0)))
                .has(containsSubstring("217", atIndex(1)));
Run Code Online (Sandbox Code Playgroud)

但也许你应该简单地写一个循环:

List<String> list = ...;
List<String> expectedSubstrings = Arrays.asList("15", "217");
for (int i = 0; i < list.size(); i++) {
    assertThat(list.get(i)).contains(expectedSubstrings.get(i));
}
Run Code Online (Sandbox Code Playgroud)

或者编写一个参数化测试,以便 JUnit 本身在每个子字符串上测试每个元素。