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

82

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

Предположим, у меня есть 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пустым. Я думаю, вы можете использовать это, чтобы получить то, что хотите.
СудоРахул
6
containsAllи removeAllпредназначены O(n²)для списков при их сортировке, а проверка на равенство - O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));тоже чистый.
Alexis C.
1
возможный дубликат коллекции сравнения Hamcrest
Джо
@SudoRahul - Что делать, если вы не хотите изменять список, удаляя все?
Erran Morad
@BoratSagdiyev - Поскольку это не было ограничением OP, я предложил это. Но если это ограничение, то принятый ответ на этот вопрос решает проблему.
СудоРахул

Ответы:

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 с assertTrue JUnit, чтобы проверить, что первый список содержит все элементы из второго, и наоборот.

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));
Робертоя
источник
2
@kukis Это зависит от того, хотите ли вы проверить дубликаты?
Робертоя
4
Да, конечно. 2 приведенных списка должны быть точно такими же, просто без учета порядка.
kukis 02
2
@kukis Тогда проверьте комментарий ZouZou к вашему вопросу.
Робертоя
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 упоминает CollectionUtils.isEqualCollection () Apache Commons Collections, которая в этом примере правильно определяет, что коллекции не равны.
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");
    }    
}
davidxxx
источник
3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);
Tinyfool
источник
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 принятое решение имеет ту же проблему. Вы предложили единственно верное решение. Если вы ответите, я проголосую за него.
leventov 02
@ZouZou Это не тот же список, но они содержат точно такие же строки. ОП, проясните ?. Кроме того, дайте ответ, и я тоже буду голосовать :) не подумал об этом.
Робертоя
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"); Список <String> list2 = Arrays.asList («a», «b», «b»); Assert.assertTrue (list1.containsAll (list2)); Assert.assertTrue (list2.containsAll (list1)); }
Erran Morad
1

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

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;
}
Дэниел Эйвери
источник