在 Java 中断言两个列表的相等忽略顺序

评论 0 浏览 0 2020-08-31

1.概述

有时在编写单元测试时,我们需要对列表进行顺序无关的比较。在这个简短的教程中,我们将看一下如何编写这种单元测试的不同例子。

2.设置

根据List#equalsJava文档,如果两个列表以相同的顺序包含相同的元素,它们就是相等的。因此,我们不能仅仅使用equals方法,因为我们想做不考虑顺序的比较。

在本教程中,我们将使用这三个列表作为我们测试的输入示例。

List first = Arrays.asList(1, 3, 4, 6, 8);
List second = Arrays.asList(8, 1, 6, 3, 4);
List third = Arrays.asList(1, 3, 3, 6, 6);

有不同的方法可以进行与顺序无关的比较。让我们逐一看一下。

3.使用JUnit

JUnit是一个众所周知的框架,用于在Java生态系统中进行单元测试。

我们可以使用下面的逻辑,用assertTrueassertFalse方法来比较两个列表的相等性。

这里我们检查两个列表的大小,并检查第一个列表是否包含第二个列表的所有元素,反之亦然。虽然这个方案可行,但它的可读性不强。所以现在让我们看看一些替代方案。

@Test
public void whenTestingForOrderAgnosticEquality_ShouldBeTrue() {
    assertTrue(first.size() == second.size() && first.containsAll(second) && second.containsAll(first));
}

在这第一个测试中,在我们检查两个列表中的元素是否相同之前,要比较两个列表的大小。由于这两个条件都返回真,我们的测试将通过。

现在让我们来看看一个失败的测试。

@Test
public void whenTestingForOrderAgnosticEquality_ShouldBeFalse() {
    assertFalse(first.size() == third.size() && first.containsAll(third) && third.containsAll(first));
}

与此相反,在这个版本的测试中,虽然两个列表的大小相同,但所有元素都不匹配。

4.使用AssertJ

AssertJ是一个由社区驱动的开源库,用于在Java测试中编写流畅而丰富的断言。

为了在我们的maven项目中使用它,让我们在pom.xml文件中添加assertj-core依赖。

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.16.1</version>
</dependency>

让我们写一个测试来比较两个相同元素和相同大小的列表实例的相等性。

@Test
void whenTestingForOrderAgnosticEqualityBothList_ShouldBeEqual() {
    assertThat(first).hasSameElementsAs(second);
}

在这个例子中,我们验证了first包含了给定的可迭代的所有元素,而没有其他的,顺序不限。这种方法的主要限制是hasSameElementsAs方法会忽略重复的元素。

让我们在实践中看看这一点,看看我们的意思是什么。

@Test
void whenTestingForOrderAgnosticEqualityBothList_ShouldNotBeEqual() {
    List a = Arrays.asList("a", "a", "b", "c");
    List b = Arrays.asList("a", "b", "c");
    assertThat(a).hasSameElementsAs(b);
}

在这个测试中,虽然我们有相同的元素,但两个列表的大小是不相等的,但断言仍然是真的,因为它忽略了重复的内容。为了使其工作,我们需要为两个列表添加一个大小检查。

assertThat(a).hasSize(b.size()).hasSameElementsAs(b);

在方法hasSameElementsAs之后添加对我们两个列表的大小的检查,确实会像预期的那样失败。

5.使用Hamcrest

如果我们已经在使用Hamcrest或者想用它来编写单元测试,下面是我们如何使用Matchers#containsInAnyOrder方法来进行不考虑顺序的比较。

为了在我们的maven项目中使用Hamcrest,让我们在pom.xml文件中添加hamcrest-all的依赖性。

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>

让我们来看看这个测试吧。

@Test
public void whenTestingForOrderAgnosticEquality_ShouldBeEqual() {
    assertThat(first, Matchers.containsInAnyOrder(second.toArray()));
}

在这里,方法containsInAnyOrderIterables创建了一个顺序无关的匹配器,它对被检查的Iterable元素进行匹配。这个测试匹配两个列表的元素,忽略了列表中元素的顺序。

值得庆幸的是,这个解决方案没有出现上一节中解释的问题,所以我们不需要明确地比较大小。

6.使用Apache Commons

除了JUnit、Hamcrest或AssertJ之外,我们可以使用的另一个库或框架是Apache CollectionUtils。它为常见的操作提供了实用的方法,涵盖了广泛的使用情况,帮助我们避免编写模板代码。

为了在我们的maven项目中使用它,让我们在pom.xml文件中添加commons-collections4的依赖性。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

这里是一个使用CollectionUtils的测试。

@Test
public void whenTestingForOrderAgnosticEquality_ShouldBeTrueIfEqualOtherwiseFalse() {
    assertTrue(CollectionUtils.isEqualCollection(first, second));
    assertFalse(CollectionUtils.isEqualCollection(first, third));
}

如果给定的集合恰好包含相同的元素,并且具有相同的cardinalities,那么isEqualCollection 方法返回true。否则,它返回false

7.结语

在这篇文章中,我们探讨了如何检查两个List实例的相对性,其中两个列表的元素的排序是不同的。

所有这些例子都可以在GitHub上找到。

最后更新2022-12-31
0 个评论
标签