Khẳng định bằng giữa 2 Danh sách trong Junit


164

Làm cách nào tôi có thể đưa ra khẳng định bình đẳng giữa các danh sách trong trường hợp kiểm tra JUnit ? Bình đẳng nên giữa các nội dung của danh sách.

Ví dụ:

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
Tôi thích sử dụng assertArrayEqualsngày nay. Sử dụng kết hợp với List#toArray.
Thibstars 6/2/18

@Thibstars - Tôi muốn đưa ra câu trả lời.
dfrankow

2
assertArrayEqualsyêu cầu bạn có được mảng từ danh sách của bạn. Bạn có thể thao tác trực tiếp trên danh sách bằng cách sử dụngassertIterableEquals
ThetaSinner

assertIterableEqualscó sẵn cho jUnit5 @ThetaSinner
iec2011007

Câu trả lời:


168

Tôi nhận ra điều này đã được hỏi một vài năm trước đây, có lẽ tính năng này không xuất hiện sau đó. Nhưng bây giờ, thật dễ dàng để làm điều này:

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

Nếu bạn có phiên bản Junit gần đây được cài đặt với hamcrest, chỉ cần thêm các nhập này:

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);sẽ trả về false, nhưng System.out.println(actual.equals(expected));sẽ trả về true.
Cá trê

@Catfish yeah, thật khó hiểu phải không. Tôi nghĩ rằng tôi đã chứng minh rằng trình so khớp đang sử dụng .equals(..)thay vì ==?
djeikyb

2
Làm thế nào là tốt hơn so với assertEquals?
Raedwald

1
@Raedwald đầu ra khi xác nhận thất bại. Tôi sẽ cố gắng quay lại sau để chỉnh sửa sự khác biệt. bộ so khớp hamcrest có thể tạo ra các thông báo lỗi chi tiết. Junit có thể đơn giản là quá tải assertEquals với lòng tốt tương tự. nhưng chủ yếu là Junit cung cấp các tính năng kiểm tra đơn vị cốt lõi và hamcrest cung cấp một thư viện mô tả khác biệt đối tượng dễ có.
djeikyb

29

Đừng biến đổi thành chuỗi và so sánh. Điều này là không tốt cho nước hoa. Trong Junit, bên trong Corematchers, có một người mai mối cho việc này =>hasItems

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

Đây là cách tốt hơn mà tôi biết để kiểm tra các yếu tố trong danh sách.


2
Nên là câu trả lời được chọn, với một lưu ý: Bạn cũng cần xác minh rằng không có thêm mục nào trong danh sách ngoài những gì bạn muốn. Có thể sử dụng:Assert.assertEquals(4,yourList.size());
yoni

hoặc với một dòng duy nhất:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602

3
"Điều này không tốt cho sự hoàn hảo." → Tôi không chắc người ta nên tính đến hiệu suất ở mức độ nào khi viết bài kiểm tra đơn vị ... Nhưng chắc chắn, so sánh chuỗi qua toString()phiên bản của họ không phải là cách tốt nhất.
walen

21

Đây là một câu trả lời kế thừa, phù hợp với JUnit 4.3 trở xuống. Phiên bản hiện đại của JUnit bao gồm các thông báo lỗi có thể đọc được trong phương thức assertThat. Thích câu trả lời khác cho câu hỏi này, nếu có thể.

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

Đối với hồ sơ, như @Paul đã đề cập trong bình luận của mình cho câu trả lời này, hai Lists bằng nhau:

nếu và chỉ khi đối tượng được chỉ định cũng là một danh sách, cả hai danh sách có cùng kích thước và tất cả các cặp phần tử tương ứng trong hai danh sách đều bằng nhau. (Hai phần tử e1e2bằng nhau nếu (e1==null ? e2==null : e1.equals(e2)).) Nói cách khác, hai danh sách được xác định là bằng nhau nếu chúng chứa các phần tử giống nhau theo cùng một thứ tự. Định nghĩa này đảm bảo rằng phương thức bằng hoạt động đúng trong các triển khai khác nhau của Listgiao diện.

Xem JavaDocs của Listgiao diện .


1
Vì vậy, bạn có nghĩa là kỳ vọng.equals (a) sẽ đảm nhận việc xác nhận các đối tượng mà danh sách đang nắm giữ?
Kamal

1
Từ danh sách javadoc: So sánh đối tượng được chỉ định với danh sách này cho sự bình đẳng. Trả về true nếu và chỉ khi đối tượng được chỉ định cũng là một danh sách, cả hai danh sách có cùng kích thước và tất cả các cặp phần tử tương ứng trong hai danh sách đều bằng nhau. (Hai phần tử e1 và e2 bằng nhau nếu (e1 == null? E2 == null: e1.equals (e2)).) Nói cách khác, hai danh sách được xác định là bằng nhau nếu chúng có cùng các phần tử theo cùng một thứ tự . Định nghĩa này đảm bảo rằng phương thức bằng hoạt động đúng trong các triển khai khác nhau của giao diện Danh sách.
Paul McKenzie

1
Than ôi này cung cấp ít hơn thông báo lỗi hữu ích. Tôi đã thấy tốt hơn khi viết một lớp tiện ích thực hiện một vòng lặp để bạn có thể thấy các phần tử nào khác nhau.
Michael Lloyd Lee mlk

@mlk, có lẽ, nhưng tôi ngần ngại viết một phương thức tiện ích tùy chỉnh cho một thứ như vậy. Điều gì về thông báo lỗi tôi vừa chỉnh sửa?
Bart Kiers

@mlk Tôi đồng ý rằng có thể tốt hơn khi viết một vòng lặp để kiểm tra từng yếu tố để bạn biết chính xác những gì khác nhau. Nó phụ thuộc vào loại đối tượng trong danh sách. Nếu chúng là Chuỗi, thì chỉ cần nói "a =" + a sẽ ổn, nhưng nếu chúng là các đối tượng phức tạp (danh sách khác hoặc thứ gì đó không có triển khai toString tốt), có thể dễ dàng kiểm tra chúng hơn
Matt N

20

assertEquals(Object, Object)từ JUnit4 / JUnit 5 hoặc assertThat(actual, is(expected));từ Hamcrest được đề xuất trong các câu trả lời khác sẽ chỉ hoạt động khi cả hai equals()toString()được ghi đè cho các lớp (và sâu) của các đối tượng được so sánh.

Nó quan trọng bởi vì kiểm tra đẳng thức trong khẳng định dựa vào equals()và thông báo lỗi kiểm tra phụ thuộc vào toString()các đối tượng được so sánh.
Đối với các lớp dựng sẵn như String, Integervà vì vậy ... không có vấn đề gì vì các lớp này ghi đè cả equals()toString(). Vì vậy, nó là hoàn toàn hợp lệ để khẳng định List<String>hoặc List<Integer>với assertEquals(Object,Object).
Và về vấn đề này: bạn phải ghi đè equals()trong một lớp vì nó có ý nghĩa về mặt bình đẳng đối tượng, không chỉ giúp cho các xác nhận dễ dàng hơn trong một thử nghiệm với JUnit.
Để làm cho các xác nhận dễ dàng hơn, bạn có những cách khác.
Là một thực hành tốt, tôi ủng hộ các thư viện khẳng định / so khớp.

Đây là một giải pháp AssertJ .

org.assertj.core.api.ListAssert.containsExactly() là những gì bạn cần: nó xác minh rằng nhóm thực tế chứa chính xác các giá trị đã cho và không có gì khác, theo thứ tự như đã nêu trong javadoc.

Giả sử một Foolớp nơi bạn thêm các phần tử và nơi bạn có thể nhận được phần tử đó.
Một bài kiểm tra đơn vị Fookhẳng định rằng hai danh sách có cùng nội dung có thể trông giống như:

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

Một điểm tốt của AssertJ là việc khai Listbáo như mong đợi là không cần thiết: nó làm cho khẳng định trở nên cứng hơn và mã dễ đọc hơn:

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

Nhưng các thư viện Ass ass / matcher là bắt buộc bởi vì những thư viện này sẽ thực sự xa hơn.
Giả sử bây giờ Foo không lưu trữ Strings nhưng Bars.
Đó là một nhu cầu rất phổ biến. Với AssertJ, khẳng định vẫn đơn giản để viết. Tốt hơn là bạn có thể khẳng định rằng nội dung danh sách là bằng nhau ngay cả khi lớp của các phần tử không ghi đè equals()/hashCode()trong khi cách JUnit yêu cầu:

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

Nếu bạn không quan tâm đến thứ tự của các yếu tố, tôi khuyên bạn nên sử dụng ListAssert.assertEqualsphần bổ trợ.

Liên kết: http://junit-addons.sourceforge.net/

Dành cho người dùng Maven lười biếng:

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

7
Lưu ý: Nếu bạn không quan tâm đến thứ tự của các yếu tố, bạn nên sử dụng Bộ hoặc Bộ sưu tập, không phải Danh sách.
Barett

11
Tôi đồng ý. Thư viện này là thô. Tại sao trên trái đất ListAssert.assertEquals () mặc định là không có trật tự?
Ryan

5

Bạn có thể sử dụng assertEquals trong 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);
    }

Nếu thứ tự của các phần tử là khác nhau thì nó sẽ trả về lỗi.

Nếu bạn đang xác nhận một danh sách đối tượng mô hình thì bạn nên ghi đè phương thức bằng trong mô hình cụ thể.

    @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

nếu bạn không muốn xây dựng một danh sách mảng, bạn cũng có thể thử điều này

@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

Đừng phát minh lại bánh xe!

Có thư viện Google Code thực hiện điều này cho bạn: Hamcrest

[Hamcrest] Cung cấp một thư viện các đối tượng khớp (còn được gọi là các ràng buộc hoặc biến vị ngữ) cho phép các quy tắc 'khớp' được định nghĩa theo cách khai báo, được sử dụng trong các khung khác. Các kịch bản điển hình bao gồm khung kiểm tra, thư viện giả và quy tắc xác thực UI.


0

Tôi biết đã có nhiều lựa chọn để giải quyết vấn đề này, nhưng tôi muốn làm như sau để khẳng định hai danh sách trong bất kỳ oder nào :

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

điều này sẽ không thất bại nếu thứ tự không phù hợp ??
iec2011007

0

assertEquals(expected, result);làm việc cho tôi Vì hàm này có hai đối tượng, bạn có thể truyền bất cứ thứ gì cho nó.

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

-1

Tôi không biết tất cả các câu trả lời trên đang đưa ra giải pháp chính xác để so sánh hai danh sách Đối tượng. Hầu hết các cách tiếp cận trên có thể hữu ích trong giới hạn so sánh chỉ sau - So sánh kích thước - So sánh tham chiếu

Nhưng nếu chúng ta có các danh sách đối tượng có cùng kích thước và dữ liệu khác nhau ở cấp độ đối tượng thì phương pháp so sánh này sẽ không hữu ích.

Tôi nghĩ rằng cách tiếp cận sau đây sẽ hoạt động hoàn hảo với ghi đè bằng và phương thức mã băm trên đối tượng do người dùng xác định.

Tôi đã sử dụng lib Xstream để ghi đè bằng và mã băm nhưng chúng tôi cũng có thể ghi đè bằng và mã băm bằng cách ra logic / so sánh thắng.

Dưới đây là ví dụ để bạn tham khảo

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

Điều quan trọng nhất là bạn có thể bỏ qua các trường bằng Chú thích (@XStreamOmitField) nếu bạn không muốn bao gồm bất kỳ trường nào trong phần kiểm tra đối tượng bằng nhau. Có nhiều chú thích như thế này để cấu hình, vì vậy hãy nhìn sâu vào các chú thích của lib này.

Tôi chắc chắn câu trả lời này sẽ tiết kiệm thời gian của bạn để xác định cách tiếp cận chính xác để so sánh hai danh sách các đối tượng :). Hãy bình luận nếu bạn thấy bất kỳ vấn đề về điều này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.