Assert дорівнює між двома списками в 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));поверне справжнє.
Сом

@Catfish Так, це заплутано чи не так. Я думаю, я демонстрував, що матч використовує .equals(..)замість ==?
djeikyb

2
Як це краще, ніж стверджувати рівності?
Raedwald

1
@Raedwald вихід, коли твердження не вдається. я спробую повернутися пізніше, щоб відредагувати різницю. Матчі hamcrest можуть генерувати детальні повідомлення про помилки. Юніт може просто перевантажувати ствердження рівнянь з подібною добротою. але в основному junit надає основні функції тестування основних одиниць, а hamcrest забезпечує бібліотеку описувальних різниць об'єктів, що є приємною.
djeikyb

29

Не перетворюйте на рядок і порівнюйте. Це не добре для виступу. У джуніті, всередині 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());
yoni

або з одним рядком:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602

3
"Це не добре для виступу". → Я не впевнений, наскільки слід враховувати продуктивність при написанні одиничних тестів ... Але впевнений, порівняння рядків через їх toString()версію - не найкращий спосіб.
walen

21

Це застаріла відповідь, підходить для JUnit 4.3 та нижче. Сучасна версія JUnit включає вбудовані читабельні повідомлення про відмову у методі assrtThat. Віддайте перевагу іншим відповідям на це питання, якщо це можливо.

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)).) Іншими словами, два списки визначаються рівними, якщо вони містять однакові елементи в одному порядку. Це визначення забезпечує, що метод рівних працює належним чином у різних реалізаціях Listінтерфейсу.

Дивіться інтерфейс JavaDocsList .


1
Отже, ви маєте на увазі очікувані.equals (a) подбають про затвердження об'єктів, у яких є список?
Камаль

1
Із списку javadoc: порівнює вказаний об'єкт із цим списком для рівності. Повертає істину, якщо і лише якщо вказаний об'єкт також є списком, обидва списки мають однаковий розмір, і всі відповідні пари елементів у двох списках рівні. (Два елементи e1 і e2 рівні, якщо (e1 == null? E2 == null: e1.equals (e2)). Іншими словами, два списки визначаються рівними, якщо вони містять однакові елементи в одному порядку . Це визначення гарантує, що метод рівних працює належним чином у різних реалізаціях інтерфейсу List.
Пол Маккензі

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

@mlk, можливо, але я не вагаюся написати спеціальний корисний метод для такої речі. А що з повідомленням про помилку, яке я редагував саме зараз?
Барт Кіерс

@mlk Я погоджуюся, що, можливо, краще написати цикл для тестування кожного елемента, щоб ви точно знали, що відрізняється. Це залежить від того, які типи об’єктів є у списку. Якщо вони є Strings, то просто скажіть "a =" + a має бути добре, але якщо це складні об'єкти (інші списки або щось, що не має гарної реалізаціїString), можливо, буде простіше перевірити їх окремо
Matt 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");

Але бібліотеки затвердження / відповідників є обов'язковими, тому що це дійсно далі.
Припустимо, зараз 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

Ви можете використовувати assrtEquals у червні.

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

Якщо порядок елементів інший, то він поверне помилку.

Якщо ви стверджуєте список об'єктів моделі, тоді вам слід перекрити метод рівних у конкретній моделі.

    @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, яка робить це за вас: 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

Я цього не знаю, всі наведені вище відповіді дають точне рішення для порівняння двох списків Об'єктів. Більшість вищезазначених підходів можуть бути корисними лише при наступному обмеженні порівнянь - Порівняння розмірів - Порівняння порівнянь

Але якщо у нас є однакові за розмірами списки об'єктів і різні дані на рівні об’єктів, то це підходи порівняння не допоможуть.

Я думаю, що наступний підхід буде відмінно працювати з переважаючими рівняннями та методом хеш-коду на визначеному користувачем об’єкті.

Я використовував Xstream lib для переопределення рівних та хеш-коду, але ми можемо замінити рівні та хеш-коди, також вигравши логіку / порівняння.

Ось приклад для вашої довідки

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

Найголовніше - ви можете ігнорувати поля за допомогою Анотації (@XStreamOmitField), якщо не хочете включати жодні поля в рівну перевірку Об'єктів. Існує багато подібних анотацій для налаштування, тому глибоко ознайомтеся з анотаціями цього файлу.

Я впевнений, що ця відповідь заощадить ваш час на визначення правильного підходу для порівняння двох списків об’єктів :). Будь ласка, коментуйте, якщо ви бачите якісь проблеми з цього приводу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.