首页 > 解决方案 > Hamcrest 集合匹配,其中列表中的对象具有与另一个列表中的值匹配的字段

问题描述

我有一个正在测试的方法,它返回一个对象列表......例如“Person”对象。

我有一个“expectedLastNames”列表来验证结果。

我目前有一个工作测试,它遍历“expectedLastNames”中的名称,并断言每个名称都包含在“Person”对象列表中。像这样(请注意以下代码片段在 Kotlin 中):

expectedLastNames.forEach { expectedLastName ->
    assertThat(listOfPeople, hasItem(hasProperty("lastName", `is`(expectedLastName.trim()))))
}

当断言通过并验证我的方法时,这很有效。但是,当测试失败时,它会非常麻烦,因为它会在遇到缺少的名称并且不断言其余名称时立即失败。我宁愿改进我的断言,一次报告所有缺失的姓名,而不是一次报告一个。

类似于以下内容:

assertThat(expectedLastNames, allItems(lastNameMatches(listOfPeople)));

我希望我的 assertionFailure 消息类似于:

期望匹配所有“Smith, Jones, Davis”的列表,但在“Person[firstName=John, lastName="Jones"], Person[firstName=Sarah, lastName= 中找不到 ["Smith", "Davis"]琼斯]"

如果结果中有其他 Person 对象的 lastName 值未包含在“expectedLastNames”中,只要“expectedLastNames”中的所有名称都在结果中表示,这应该仍然通过。

同样重要的是要知道这是一个 Junit5 参数化测试,“expectedLastNames”是传入的参数之一。所以我不能只是将姓氏硬编码到断言中。

是否有一个 Hamcrest 集合匹配器可以满足我的需求?或者如果没有,任何人都可以让我开始使用可以做到的自定义匹配器吗?

任何帮助表示赞赏。

标签: junit5hamcrest

解决方案


您可以使用为 Iterable 提供的 hamcrest 匹配器来做到这一点:

import org.junit.Test;

import java.util.List;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;

public class MatchValuesInAnotherListTest {
    static class Person{
        private final String firstName;
        private final String lastName;

        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }
    }

    @Test
    public void matchLastNames() {
        List<Person> listOfPeople = asList(new Person("Paul", "Smith"), new Person("John", "Davis"));

        assertThat(listOfPeople.stream().map(Person::getLastName).collect(toList()), containsInAnyOrder("Davis", "Smith"));
        assertThat(listOfPeople.stream().map(Person::getLastName).collect(toList()), contains("Smith", "Davis"));
    }
}

他们将提供格式良好的失败消息:

java.lang.AssertionError: 
Expected: iterable over ["Smith"] in any order
     but: Not matched: "Davis"

    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)

推荐阅读