AssertEquals 2 列表忽略顺序
Posted
技术标签:
【中文标题】AssertEquals 2 列表忽略顺序【英文标题】:AssertEquals 2 Lists ignore order 【发布时间】:2014-05-13 11:49:41 【问题描述】:我相信这应该是一个非常简单的问题。但不知何故,我无法在 Google 中找到答案。
假设我有 2 个字符串列表。第一个包含 "String A" 和 "String B",第二个包含 "String B" 和 "String A"(注意顺序不同)。我想用 JUnit 对它们进行测试,以检查它们是否包含 完全相同的字符串。
是否有任何断言检查忽略顺序的字符串的相等性?对于给定的示例 org.junit.Assert.assertEquals 抛出 AssertionError
java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>
解决方法是首先对列表进行排序,然后将它们传递给断言。但我希望我的代码尽可能简单干净。
我使用 Hamcrest 1.3、JUnit 4.11、Mockito 1.9.5。
【问题讨论】:
list1.removeAll(list2)
应将list1
留空。我想你可以在此基础上得到你想要的。
containsAll
和 removeAll
是 O(n²)
用于列表,同时对它们进行排序并测试是否相等是 O(nlogn)
。 Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));
也很干净。
Hamcrest compare collections的可能重复
@SudoRahul - 如果您不想通过删除所有来修改列表怎么办?
@BoratSagdiyev - 由于这不是 OP 的限制,我建议这样做。但如果这是一个限制条件,那么这个问题的公认答案就可以解决手头的问题。
【参考方案1】:
正如您提到的,您使用 Hamcrest, 所以我会选择一个集合匹配器
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertThat;
public class CompareListTest
@Test
public void compareList()
List<String> expected = Arrays.asList("String A", "String B");
List<String> actual = Arrays.asList("String B", "String A");
assertThat("List equality without order",
actual, containsInAnyOrder(expected.toArray()));
【讨论】:
另请参阅我的回答 ***.com/a/38262680/297710,其中显示了如何改进 Hamcrest 匹配器并在每个使用 containsInAnyOrder 的断言中避免“.toArray()”【参考方案2】:您可以使用 List.containsAll 和 JUnit 的 assertTrue 来检查第一个列表是否包含第二个列表中的每个元素,反之亦然。
assertEquals(expectedList.size(), actualList.size());
assertTrue(expectedList.containsAll(actualList));
assertTrue(actualList.containsAll(expectedList));
提示: 这不适用于列表中的重复项。
【讨论】:
@kukis 这取决于,您要检查重复项吗? 是的,当然。 2 个给定的列表必须完全相同,只是忽略顺序。 @kukis 那么请查看 ZouZou 对您的问题的评论。 ..可能包括assertEquals(first.size(), second.size())
..那么它应该可以按预期工作
这不适用于列表中的重复项。下面是一个示例来演示:List<String> list1 = Arrays.asList("a", "a", "b");
List<String> list2 = Arrays.asList("a", "b", "b");
assertEquals(list1.size(), list2.size());
assertTrue(list1.containsAll(list2) && list2.containsAll(list1));
在这个示例中,两个断言都无法检测到列表实际上是不同的。 @AlexWorden 提到了 Apache Commons Collections 的 CollectionUtils.isEqualCollection(),在这个例子中,它正确地检测到集合不相等。【参考方案3】:
这是一种避免二次复杂性(多次迭代列表)的解决方案。这使用 Apache Commons CollectionUtils 类来创建每个项目的映射到列表中的频率计数本身。然后它只是比较两个地图。
Assert.assertEquals("Verify same metrics series",
CollectionUtils.getCardinalityMap(expectedSeriesList),
CollectionUtils.getCardinalityMap(actualSeriesList));
我也刚刚发现 CollectionUtils.isEqualCollection 声称完全按照这里的要求做......
https://commons.apache.org/proper/commons-collections/apidocs/index.html?org/apache/commons/collections4/CollectionUtils.html
【讨论】:
【参考方案4】:使用 AssertJ,containsExactlyInAnyOrder()
或 containsExactlyInAnyOrderElementsOf()
就是您所需要的:
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
public class CompareListTest
@Test
public void compareListWithTwoVariables()
List<String> expected = Arrays.asList("String A", "String B");
List<String> actual = Arrays.asList("String B", "String A");
Assertions.assertThat(actual)
.containsExactlyInAnyOrderElementsOf(expected);
@Test
public void compareListWithInlineExpectedValues()
List<String> actual = Arrays.asList("String B", "String A");
Assertions.assertThat(actual)
.containsExactlyInAnyOrder("String A", "String B");
【讨论】:
【参考方案5】: Collections.sort(excepted);
Collections.sort(actual);
assertEquals(excepted,actual);
【讨论】:
【参考方案6】:我迟到了,但这是我仅使用 Junit 的解决方案。欢迎任何想法。
List<String> actual = new ArrayList<>();
actual.add("A");
actual.add("A");
actual.add("B");
List<String> expected = new ArrayList<>();
actual.add("A");
actual.add("B");
actual.add("B");
//Step 1: assert for size
assertEquals(actual.size(), expected.size());
//Step 2: Iterate
for(String e: expected)
assertTrue(actual.contains(e));
actual.remove(e);
【讨论】:
【参考方案7】:请注意,Roberto Izquierdo 的解决方案通常具有二次复杂度。 HashSets 的解决方案总是具有线性复杂度:
assertTrue(first.size() == second.size() &&
new HashSet(first).equals(new HashSet(second)));
【讨论】:
这种方法行不通。如果第一个是 ("String A") 而第二个是 ("String A", "String A") 它们不是同一个列表。 您无法检查大小。如果第一个是("s1", "s2", "s3" ,"s1")
,第二个是("s2", "s1", "s3" ,"s2");
,它们不是同一个列表。
@ZouZou 接受的解决方案有同样的问题。您提出了唯一真正正确的解决方案。如果你回答我会投票赞成。
@ZouZou 它们不是同一个列表,但它们包含完全相同的字符串。 OP,澄清?另外,让它成为一个答案,我也会投票赞成:) 没想到。
这对于所有情况(“A”、“A”、“B”)仍然不正确,将比较等于(“A”、“B”、“B”)【参考方案8】:
为了快速修复,我会检查两种方式:
assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));
在尝试相同元素的数量不同(例如 1、1、2 和 1、2、2)的情况下,我没有得到误报。
【讨论】:
您的代码仍然失败。看这个例子 - @Test public void test1() List您可以使用 junit-addons jar 中的ListAssert。
ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));
【讨论】:
【参考方案10】:看起来其他答案要么引用了第 3 方实用程序,要么不正确,要么效率低下。
这是 Java 8 中的 O(N) vanilla 解决方案。
public static void assertContainsSame(Collection<?> expected, Collection<?> actual)
assert expected.size() == actual.size();
Map<Object, Long> counts = expected.stream()
.collect(Collectors.groupingBy(
item -> item,
Collectors.counting()));
for (Object item : actual)
assert counts.merge(item, -1L, Long::sum) != -1L;
【讨论】:
以上是关于AssertEquals 2 列表忽略顺序的主要内容,如果未能解决你的问题,请参考以下文章