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.