AssertEquals2リストは順序を無視します


82

それは本当に簡単な質問だと思います。しかし、どういうわけか私はグーグルで答えを見つけることができません。

文字列のリストが2つあると仮定します。最初の文字列に「文字列A」と「文字列B」が含まれ、2番目の文字列に「文字列B」と「文字列A」が含まれます(順序の違いに注意してください)。それらをJUnitでテストして、まったく同じ文字列が含まれているかどうかを確認したいと思います。

順序を無視する文字列の同等性をチェックするアサーションはありますか?与えられた例では、org.junit.Assert.assertEqualsはAssertionErrorをスローします

java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>

回避策は、最初にリストをソートしてから、それらをアサーションに渡すことです。しかし、私は自分のコードをできるだけシンプルでクリーンにしたいと思っています。

Hamcrest 1.3JUnit 4.11Mockito1.9.5を使用しています。


3
list1.removeAll(list2)list1空のままにする必要があります。私はあなたがあなたが望むものを得るためにこれに基づいて構築することができると思います。
SudoRahul 2014

6
containsAllremoveAllO(n²)リストをソートする際のリストであり、同等性をテストするのはO(nlogn)です。Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));きれいでもあります。
Alexis C.


@ SudoRahul-すべてを削除してリストを変更したくない場合はどうなりますか?
Erran Morad 2017

@ BoratSagdiyev-それはOPからの制約ではなかったので、私はそれを提案しました。しかし、それが制約である場合、この質問に対して受け入れられた答えは、目前の問題を解決します。
SudoRahul 2017

回答:


92

あなたがハムクレストを使用しているとあなたが言うように、私はコレクションマッチャーの1つを選びます

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()));
    }

}

5
ハムクレストマッチャーを改善し、containsInAnyOrder
yvolk 2016

57

List.containsAllをJUnitのassertTrueとともに使用して、最初のリストに2番目のリストのすべての要素が含まれていることを確認できます。その逆も可能です。

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));

2
@kukis状況によりますが、重複をチェックしますか?
robertoia 2014

4
はい、もちろん。与えられた2つのリストは、順序を無視するだけでまったく同じでなければなりません。
kukis 2014

2
@kukisそれではあなたの質問に対するZouZouのコメントをチェックしてください。
robertoia 2014

1
assertEquals(first.size(), second.size())..含まれている可能性があります..その後、期待どおりに機能するはずです
間違いなく定義

17
これは、リスト内の重複では機能しません。示す例を次に示します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()について言及しています。この例では、コレクションが等しくないことを正しく検出します。
desilvai 2016年

11

これは、2次の複雑さを回避するソリューションです(リストを複数回繰り返す)。これは、Apache Commons CollectionUtilsクラスを使用して、リスト内の頻度カウント自体への各アイテムのマップを作成します。次に、2つのマップを単純に比較します。

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");
    }    
}

3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

2

パーティーに遅れましたが、これが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);
}

1

Roberto Izquierdoによるソリューションは、一般に2次の複雑さを持っていることに注意してください。HashSetsのソリューションには、常に線形の複雑さがあります。

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));

2
そのアプローチは機能しません。最初が( "String A")で、2番目が( "String A"、 "String A")の場合、それらは同じリストではありません。
Alexis C.

4
サイズは確認できません。最初がで("s1", "s2", "s3" ,"s1")、2番目が("s2", "s1", "s3" ,"s2");である場合、それらは同じリストではありません。
Alexis C.

@ZouZou受け入れられた解決策にも同じ問題があります。あなたは本当に正しい解決策を提案しました。あなたが答えたら私はそれを賛成します。
leventov 2014

@ZouZouこれらは同じリストではありませんが、まったく同じ文字列が含まれています。OP、明確にしますか?また、それを答えにしてください、そして私も賛成します:)それについては考えませんでした。
robertoia 2014

2
これはまだすべての場合に正しいわけではありません( "A"、 "A"、 "B")は( "A"、 "B"、 "B")と等しいと比較されます
Tim B

1

簡単な修正のために、私は両方の方法をチェックします:

assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));

また、同じ要素の数が異なる状況(1、1、2と1、2、2など)で試してみても、誤検知は発生しませんでした。


1
それでもコードは失敗します。この例を参照してください-@ Test public void test1(){List <String> list1 = Arrays.asList( "a"、 "a"、 "b"); List <String> list2 = Arrays.asList( "a"、 "b"、 "b"); Assert.assertTrue(list1.containsAll(list2)); Assert.assertTrue(list2.containsAll(list1)); }
Erran Morad 2017


0

他の回答は、サードパーティのユーティリティを参照しているか、正しくないか、非効率的であるように見えます。

これがJava8のO(N)バニラソリューションです。

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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.