AssertEquals 2 Списки ігнорують порядок


82

Я вважаю, це справді просте запитання. Але я якось не можу знайти відповіді в Google.

Припустимо, що у мене є 2 списки рядків. Перший містить "Рядок A" і "Рядок B" , другий містить "Рядок B" і "Рядок 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 .


3
list1.removeAll(list2)слід залишити list1порожнім. Думаю, ви можете на цьому спиратися, щоб отримати бажане.
SudoRahul

6
containsAllі removeAllпризначені O(n²)для списків під час їх сортування та перевірки на рівність O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));також чисто.
Alexis C.

1
можливий дублікат колекцій порівняння Hamcrest
Джо

@SudoRahul - Що робити, якщо ви не хочете змінювати список, видаляючи все?
Ерран Морад

@BoratSagdiyev - Оскільки це не було обмеженням з боку OP, я запропонував це. Але якщо це є обмеженням, то прийнята відповідь на це питання вирішує актуальну проблему.
SudoRahul

Відповіді:


92

Коли ви згадуєте, що використовуєте Hamcrest, я вибрав би одну з колекції Matchers

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
Дивіться також мою відповідь stackoverflow.com/a/38262680/297710, яка показує, як покращити збіги Hamcrest та уникнути ".toArray ()" у кожному твердженні з containsInAnyOrder
yvolk

57

Ви можете використовувати List.containsAll з JUnit's assertTrue, щоб перевірити, чи містить перший список усі елементи з другого, і навпаки.

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

2
@kukis Це залежить, чи хочете ви перевірити наявність дублікатів?
robertoia

4
Так, звісно. 2 подані Списки мають бути абсолютно однаковими, лише ігноруючи порядок.
kukis 02

2
Тоді перевірте коментар ZouZou до вашого запитання.
robertoia

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

11

Ось рішення, яке дозволяє уникнути квадратичної складності (перебирання списків кілька разів). Це використовує клас 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");
    }    
}


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

Зверніть увагу, що рішення Роберто Іск'єрдо має загальну складність. Рішення на HashSets завжди має лінійну складність:

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

2
Такий підхід не спрацює. Якщо перший ("Рядок A"), а другий ("Рядок A", "Рядок A"), це не однакові списки.
Alexis C.

4
Ви не можете перевірити розмір. Якщо перший є, ("s1", "s2", "s3" ,"s1")а другий - ("s2", "s1", "s3" ,"s2");це не той самий список.
Alexis C.

@ZouZou прийняте рішення має таку ж проблему. Ви запропонували єдине справді правильне рішення. Якщо ви дасте відповідь, я підтримую її.
Левентов

@ZouZou Це не той самий список, але вони містять абсолютно однакові рядки. ОП, уточнити ?. Крім того, дайте відповідь, і я також підтримую :) не думав про це.
robertoia

2
Це все ще не правильно для всіх випадків ("A", "A", "B") буде порівняно як рівне ("A", "B", "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)); }
Ерран Морад

1

Ви можете використовувати ListAssert, який поставляється в jarit-addons jar.

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));

0

Схоже, інші відповіді посилаються на використання сторонніх виробників, є неправильними або неефективними.

Ось розчин ванілі O (N) у Java 8.

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.