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

Я считаю, что это должен быть действительно простой вопрос. Но почему-то не могу найти ответ в гугле.

Предположим, что у меня есть 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.


person kukis    schedule 02.04.2014    source источник
comment
list1.removeAll(list2) следует оставить list1 пустым. Я думаю, вы можете опираться на это, чтобы получить то, что вы хотите.   -  person RainMaker    schedule 02.04.2014
comment
containsAll и removeAll - это O(n²) для списков при их сортировке, а проверка на равенство - O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2)); тоже чистый.   -  person Alexis C.    schedule 02.04.2014
comment
возможный дубликат коллекций сравнения Hamcrest   -  person Joe    schedule 05.04.2014
comment
@SudoRahul - Что, если вы не хотите изменять список, удаляя все?   -  person Erran Morad    schedule 13.12.2017
comment
@BoratSagdiyev - Поскольку это не было ограничением ОП, я предложил это. Но если это ограничение, то принятый ответ на этот вопрос решает проблему.   -  person RainMaker    schedule 14.12.2017


Ответы (9)


Поскольку вы упомянули, что используете 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()));
    }

}
person cheffe    schedule 03.04.2014
comment
См. также мой ответ stackoverflow.com/a/38262680/297710, в котором показано, как улучшить сопоставители Hamcrest и избежать .toArray( ) в каждом утверждении с containsInAnyOrder - person yvolk; 12.07.2016

Вы можете использовать список .containsAll с JUnit assertTrue для проверки того, что первый список содержит все элементы из второго, и наоборот.

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));
person robertoia    schedule 02.04.2014
comment
Я принимаю этот, потому что код, на мой взгляд, самый чистый. - person kukis; 02.04.2014
comment
@kukis Смотрите комментарии в ответе Левентова. - person robertoia; 02.04.2014
comment
Правда что. Не знаю, какой ответ правильный. - person kukis; 02.04.2014
comment
@kukis Это зависит от того, хотите ли вы проверить дубликаты? - person robertoia; 02.04.2014
comment
Ну конечно; естественно. 2 заданных списка должны быть точно такими же, просто игнорируя порядок. - person kukis; 02.04.2014
comment
@kukis Проверьте комментарий ZouZou к вашему вопросу. - person robertoia; 02.04.2014
comment
..может включать assertEquals(first.size(), second.size()) ..тогда все должно работать как положено - person definitely undefinable; 24.05.2016
comment
Это не работает с дубликатами в списке. Вот пример для демонстрации: 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 упоминает CollectionUtils.isEqualCollection() из коллекций Apache Commons, который в этом примере правильно определяет, что коллекции не равны. - person desilvai; 30.06.2016
comment
Я надеюсь, что редактирование здесь будет иметь больше смысла, чтобы использовать этот метод для сравнения. - person Naman; 02.11.2017
comment
@nullpointer - Похоже, вы неправильно поняли комментарий desilvai. Ответ Роберто по-прежнему неверен даже после ваших правок. Пожалуйста, отмените правки и понизьте ответ Роберто. - person Erran Morad; 13.12.2017
comment
@BoratSagdiyev Не могли бы вы уточнить, что там еще не так? - person Naman; 13.12.2017
comment
@nullpointer - утверждения, которые вы добавили в свое редактирование, не смогут правильно сравнивать списки массивов. Чтобы увидеть, как это происходит, запустите код в комментарии selivai. - person Erran Morad; 13.12.2017

Вот решение, которое позволяет избежать квадратичной сложности (многократное повторение списков). При этом используется класс 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

person Alex Worden    schedule 24.05.2016

С 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");
    }    
}
person davidxxx    schedule 07.08.2019

Я опаздываю на вечеринку, но вот мое решение, использующее только 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);
}
person Boss Man    schedule 03.07.2018

Обратите внимание, что решение Роберто Искьердо в общем случае имеет квадратичную сложность. Решение на HashSets всегда имеет линейную сложность:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));
person leventov    schedule 02.04.2014
comment
Такой подход не сработает. Если первый (String A), а второй (String A, String A), это разные списки. - person Alexis C.; 02.04.2014
comment
Вы не можете проверить размер. Если первый — ("s1", "s2", "s3" ,"s1"), а второй — ("s2", "s1", "s3" ,"s2");, это не один и тот же список. - person Alexis C.; 02.04.2014
comment
@ZouZou принятое решение имеет ту же проблему. Вы предложили единственно правильное решение. Если вы сделаете ответ, я проголосую за него. - person leventov; 02.04.2014
comment
@ZouZou Это не один и тот же список, но они содержат точно такие же строки. ОП, поясни? Кроме того, сделайте это ответом, и я тоже проголосую :) не думал об этом. - person robertoia; 02.04.2014
comment
Это по-прежнему неверно для всех случаев (A, A, B) будет сравниваться как равное (A, B, B) - person Tim B; 07.02.2017

Для быстрого исправления я бы проверил оба способа:

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

И пытаясь с ситуацией, когда количество одинаковых элементов разное (например, 1, 1, 2 и 1, 2, 2), я не получил ложных срабатываний.

person Kristjan Veskimäe    schedule 02.04.2014
comment
Ваш код все еще не работает. См. этот пример: @Test public void test1() { List‹String› list1 = Arrays.asList(a, a, b); Список‹Строка› list2 = Arrays.asList(a, b, b); Assert.assertTrue (list1.containsAll (list2)); Assert.assertTrue (list2.containsAll (list1)); } - person Erran Morad; 13.12.2017

Вы можете использовать ListAssert, который входит в jar-файл junit-addons.

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));
person akungta    schedule 31.03.2015

Похоже, что другие ответы либо ссылаются на сторонние утилиты, либо неверны, либо неэффективны.

Вот ванильное решение 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;
}
person Daniel Avery    schedule 25.02.2020