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


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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.