Утверждение равно 2 спискам в Junit


165

Как я могу сделать утверждение равенства между списками в тестовом примере JUnit ? Равенство должно быть между содержанием списка.

Например:

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four"); 

// numbers should be equal to numbers2
//numbers should not be equal to numbers3

5
Я люблю использовать в assertArrayEqualsнастоящее время. Используйте в сочетании с List#toArray.
Thibstars

@Thibstars - я бы назвал это ответом.
dfrankow

2
assertArrayEqualsтребует, чтобы вы получили массивы из ваших списков. Вы можете работать непосредственно со списком, используяassertIterableEquals
ThetaSinner

assertIterableEqualsдоступно для jUnit5 @ThetaSinner
iec2011007

Ответы:


170

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

@Test
public void test_array_pass()
{
  List<String> actual = Arrays.asList("fee", "fi", "foe");
  List<String> expected = Arrays.asList("fee", "fi", "foe");

  assertThat(actual, is(expected));
  assertThat(actual, is(not(expected)));
}

Если у вас установлена ​​последняя версия Junit с Hamcrest, просто добавьте этот импорт:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)

http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html

http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html


3
System.out.println(actual == expected);вернет ложь, но System.out.println(actual.equals(expected));вернет истину.
сом

@ Катфиш, да, это сбивает с толку, не так ли? Я думаю, что я демонстрировал, что matcher использует .equals(..)вместо ==?
djikyb

2
Как это лучше, чем assertEquals?
Raedwald

1
@Raedwald вывод, когда утверждение не удается. Я постараюсь вернуться позже, чтобы изменить разницу. Hamcrest Matchers могут генерировать подробные сообщения об ошибках. для junit возможно просто перегрузить assertEquals с аналогичным качеством. но в основном junit предоставляет функции базового модульного тестирования, а hamcrest предоставляет библиотеку описателей различий между объектами.
djeikyb

29

Не преобразовывайте в строку и не сравнивайте. Это не хорошо для исполнения. В junit, внутри Corematchers, есть подходящее средство для этого =>hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

Это лучший из известных мне способов проверки элементов в списке.


2
Должен быть выбранный ответ с одной пометкой: Вам также необходимо убедиться, что в списке больше нет элементов, кроме того, что вы хотите. Может быть, использовать:Assert.assertEquals(4,yourList.size());
Йони

или одной строкой:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602

3
«Это не хорошо для исполнения». → Я не уверен, в какой степени следует учитывать производительность при написании модульных тестов ... Но уверен, что сравнение строк через их toString()версию не лучший способ.
Вален

21

Это устаревший ответ, подходящий для JUnit 4.3 и ниже. Современная версия JUnit включает в себя встроенные читаемые сообщения об ошибках в методе assertThat. Предпочитаю другие ответы на этот вопрос, если это возможно.

List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
            "\n  'a'        = "+a+
            "\n  'expected' = "+expected, 
            expected.equals(a));

Для записи, как @Paul упомянул в своем комментарии к этому ответу, два Lists равны:

если и только если указанный объект также является списком, оба списка имеют одинаковый размер, и все соответствующие пары элементов в двух списках равны. (Два элемента e1и e2равны , если (e1==null ? e2==null : e1.equals(e2)).) Другими словами, два списка определяются равными , если они содержат одни и те же элементы в том же порядке. Это определение гарантирует, что метод equals работает правильно в разных реализациях Listинтерфейса.

Смотрите JavaDocs Listинтерфейса .


1
Таким образом, вы имеете в виду ожидаемый .equals (a) позаботится об утверждении объектов, содержащихся в списке?
Камаль

1
Из списка javadoc: сравнивает указанный объект с этим списком на равенство. Возвращает true тогда и только тогда, когда указанный объект также является списком, оба списка имеют одинаковый размер, и все соответствующие пары элементов в двух списках равны. (Два элемента e1 и e2 равны, если (e1 == null? E2 == null: e1.equals (e2)).) Другими словами, два списка определяются как равные, если они содержат одинаковые элементы в одинаковом порядке. , Это определение гарантирует, что метод equals работает правильно в разных реализациях интерфейса List.
Пол Маккензи

1
Это увы дает менее чем полезное сообщение об ошибке. Я обнаружил, что лучше написать вспомогательный класс, который выполняет цикл, чтобы вы могли видеть, какие элементы отличаются.
Майкл Ллойд Ли mlk

@mlk, возможно, но я не решаюсь написать собственный метод утилиты для такой вещи. Как насчет сообщения об ошибке, которое я только что отредактировал?
Барт Киерс

@mlk Я согласен, что было бы лучше написать цикл для проверки каждого элемента, чтобы вы точно знали, что отличается. Это зависит от того, какие типы объектов находятся в списке. Если они являются строками, то просто скажите «a =» + a, но если они являются сложными объектами (другими списками или чем-то, что не имеет хорошей реализации toString), может быть проще проверить их по отдельности
Мэтт N

20

assertEquals(Object, Object)от JUnit4 / JUnit 5 или assertThat(actual, is(expected));из Hamcrest предложенных в других ответах будет работать только как equals()иtoString() переопределяются для классов (и глубоко) сравниваемых объектов.

Это важно, потому что проверка на равенство в утверждении основывается, equals()а сообщение о сбое теста опирается на toString()сравниваемые объекты.
Для встроенных классов, таких как String, Integerи так для ... нет проблем, так как они переопределяют equals()и toString(). Так что это совершенно справедливо, чтобы утверждать List<String>или List<Integer>с assertEquals(Object,Object).
И по этому поводу: вы должны переопределить equals()в классе, потому что это имеет смысл с точки зрения равенства объектов, а не только для упрощения утверждений в тесте с JUnit.
Чтобы сделать утверждения проще, у вас есть другие способы.
В качестве хорошей практики я предпочитаю библиотеки утверждений / сопоставлений.

Вот решение AssertJ .

org.assertj.core.api.ListAssert.containsExactly() это то, что вам нужно: он проверяет, что фактическая группа содержит именно заданные значения и ничего больше, в порядке, указанном в javadoc.

Предположим, Fooкласс, где вы добавляете элементы и где вы можете получить это.
В модульном тесте Fooэтого утверждается, что оба списка имеют одинаковое содержимое:

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception { 
   Foo foo = new Foo();
   foo.add("One", "Two", "Three");
   Assertions.assertThat(foo.getElements())
             .containsExactly("One", "Two", "Three");
}

Хорошая мысль AssertJ заключается в том, что объявление « Listкак ожидается» не нужно: оно делает утверждение более понятным, а код - более читабельным:

Assertions.assertThat(foo.getElements())
         .containsExactly("One", "Two", "Three");

Но библиотеки Assertion / matcher являются обязательными, потому что они действительно будут дальше.
Предположим теперь, что Foo не хранит Strings, а Bars экземпляров.
Это очень распространенная потребность. С AssertJ утверждение все еще просто написать. Лучше вы можете утверждать, что содержимое списка равно, даже если класс элементов не переопределяет, в equals()/hashCode()то время как способ JUnit требует, чтобы:

import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception {
    Foo foo = new Foo();
    foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
    Assertions.assertThat(foo.getElements())
              .extracting(Bar::getId, Bar::getName)
              .containsExactly(tuple(1, "One"),
                               tuple(2, "Two"),
                               tuple(3, "Three"));
}

7

Если вам не ListAssert.assertEqualsважен порядок элементов, я рекомендую в junit-addons.

Ссылка: http://junit-addons.sourceforge.net/

Для ленивых пользователей Maven:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

7
Примечание. Если вам не важен порядок элементов, вам следует использовать набор или коллекцию, а не список.
Баретт

11
Я согласен. Эта библиотека брутто. С какой стати ListAssert.assertEquals () по умолчанию стал бы беспорядочным?
Райан

5

Вы можете использовать assertEquals в junit.

import org.junit.Assert;   
import org.junit.Test;

    @Test
    public void test_array_pass()
    {
        List<String> actual = Arrays.asList("fee", "fi", "foe");
        List<String> expected = Arrays.asList("fee", "fi", "foe");
        Assert.assertEquals(actual,expected);
    }

Если порядок элементов отличается, он вернет ошибку.

Если вы утверждаете список объектов модели, вы должны переопределить метод equals в конкретной модели.

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj instanceof ModelName) {
            ModelName other = (ModelName) obj;
            return this.getItem().equals(other.getItem()) ;
        }
        return false;
    }

4

если вы не хотите создавать список массивов, вы можете попробовать это также

@Test
public void test_array_pass()
{
  List<String> list = Arrays.asList("fee", "fi", "foe");
  Strint listToString = list.toString();
  Assert.assertTrue(listToString.contains("[fee, fi, foe]"));   // passes  
}

3
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));

1

Не изобретай велосипед!

Есть библиотека Google Code, которая сделает это за вас: Hamcrest

[Hamcrest] Предоставляет библиотеку объектов соответствия (также называемых ограничениями или предикатами), позволяющих декларативно определять правила «соответствия» и использовать их в других средах. Типичные сценарии включают рамки тестирования, библиотеки-макеты и правила проверки пользовательского интерфейса.


0

Я знаю, что уже есть много вариантов решения этой проблемы, но я бы предпочел сделать следующее для утверждения двух списков в любой команде :

assertTrue(result.containsAll(expected) && expected.containsAll(result))

не потерпит ли это неудачу, если заказ не соответствует ??
iec2011007

0

assertEquals(expected, result);работает для меня. Поскольку эта функция получает два объекта, вы можете передать ей все что угодно.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

-1

Я не думаю, что все приведенные выше ответы дают точное решение для сравнения двух списков объектов. Большинство из вышеперечисленных подходов могут быть полезны только в следующих пределах сравнений - Сравнение размеров - Сравнение сравнений

Но если у нас есть списки объектов одинакового размера и разные данные на уровне объектов, тогда это сравнение не поможет.

Я думаю, что следующий подход будет отлично работать с переопределением equals и методом хэш-кода для пользовательского объекта.

Я использовал Xstream lib для переопределения equals и hashcode, но мы можем переопределить equals и hashcode, даже выиграв логику / сравнение.

Вот пример для вашей справки

    import com.thoughtworks.xstream.XStream;

    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.List;

    class TestClass {
      private String name;
      private String id;

      public void setName(String value) {
        this.name = value;
      }

      public String getName() {
        return this.name;
      }

      public String getId() {
        return id;
      }

      public void setId(String id) {
        this.id = id;
      }

      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      @Override
      public boolean equals(Object o) {
        XStream xstream = new XStream();
        String oxml = xstream.toXML(o);
        String myxml = xstream.toXML(this);

        return myxml.equals(oxml);
      }

      /**
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        XStream xstream = new XStream();
        String myxml = xstream.toXML(this);
        return myxml.hashCode();
      }
    }

    public class XstreamCompareTest {
      public static void main(String[] args) throws ParseException {
      checkObjectEquals();
}

      private static void checkObjectEquals() {
        List<TestClass> testList1 = new ArrayList<TestClass>();
        TestClass tObj1 = new TestClass();
        tObj1.setId("test3");
        tObj1.setName("testname3");
        testList1.add(tObj1);

        TestClass tObj2 = new TestClass();
        tObj2.setId("test2");
        tObj2.setName("testname2");
        testList1.add(tObj2);

        testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        List<TestClass> testList2 = new ArrayList<TestClass>();
        TestClass tObj3 = new TestClass();
        tObj3.setId("test3");
        tObj3.setName("testname3");
        testList2.add(tObj3);

        TestClass tObj4 = new TestClass();
        tObj4.setId("test2");
        tObj4.setName("testname2");
        testList2.add(tObj4);

        testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        if (isNotMatch(testList1, testList2)) {
          System.out.println("The list are not matched");
        } else {
          System.out.println("The list are matched");
        }

      }

      private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
        return clist1.size() != clist2.size() || !clist1.equals(clist2);
      }
    }

Самое главное, что вы можете игнорировать поля с помощью Annotation (@XStreamOmitField), если вы не хотите включать какие-либо поля в одинаковую проверку объектов. Существует множество подобных аннотаций для настройки, поэтому внимательно изучите аннотации этой библиотеки.

Я уверен, что этот ответ сэкономит ваше время, чтобы определить правильный подход для сравнения двух списков объектов :). Пожалуйста, прокомментируйте, если вы видите какие-либо проблемы по этому вопросу.

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