AssertEquals 2 Danh sách bỏ qua thứ tự


82

Tôi tin đó là câu hỏi thực sự đơn giản. Nhưng bằng cách nào đó tôi không thể tìm thấy câu trả lời trong Google.

Giả sử rằng tôi có 2 danh sách chuỗi. Đầu tiên chứa "Chuỗi A" và "Chuỗi B" , thứ hai chứa "Chuỗi B" và "Chuỗi A" (nhận thấy sự khác biệt theo thứ tự). Tôi muốn kiểm tra chúng bằng JUnit để kiểm tra xem chúng có chứa chính xác các Chuỗi giống nhau hay không .

Có khẳng định nào kiểm tra tính bình đẳng của các chuỗi bỏ qua thứ tự không? Ví dụ đã cho org.junit.Assert.assertEquals ném AssertionError

java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>

Công việc xung quanh là sắp xếp các Danh sách trước tiên và sau đó chuyển chúng đến xác nhận. Nhưng tôi muốn mã của mình càng đơn giản và sạch càng tốt.

Tôi sử dụng Hamcrest 1.3 , JUnit 4.11 , Mockito 1.9.5 .


3
list1.removeAll(list2)nên để list1trống. Tôi đoán bạn có thể dựa trên điều này để đạt được những gì bạn muốn.
SudoRahul

6
containsAllremoveAllđang O(n²)cho các danh sách trong khi sắp xếp họ và thử nghiệm cho sự bình đẳng là O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));cũng sạch sẽ.
Alexis C.

1
Có thể có bản sao của các bộ sưu tập so sánh Hamcrest
Joe

@SudoRahul - Điều gì xảy ra nếu bạn không muốn sửa đổi danh sách bằng cách xóa tất cả?
Erran Morad

@BoratSagdiyev - Vì đó không phải là hạn chế từ OP, tôi đã đề xuất điều đó. Nhưng nếu đó là một hạn chế, thì câu trả lời được chấp nhận cho câu hỏi này sẽ giải quyết được vấn đề trong tầm tay.
SudoRahul

Câu trả lời:


92

Khi bạn đề cập rằng bạn sử dụng Hamcrest, tôi sẽ chọn một trong những bộ sưu tập 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
Xem thêm câu trả lời của tôi stackoverflow.com/a/38262680/297710 cho biết, cách cải thiện trình so khớp Hamcrest và tránh ".toArray ()" trong mỗi lần xác nhận với containsInAnyOrder
yvolk

57

Bạn có thể sử dụng List.containsAll với khẳng định của JUnit để kiểm tra xem danh sách đầu tiên có chứa mọi phần tử từ danh sách thứ hai hay không và ngược lại.

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));

2
@kukis Còn tùy, bạn có muốn kiểm tra các bản sao không?
robertoia

4
Phải, tất nhiên. 2 Danh sách đã cho phải hoàn toàn giống nhau chỉ cần bỏ qua thứ tự.
kukis

2
@kukis Sau đó, hãy kiểm tra bình luận của ZouZou về câu hỏi của bạn.
robertoia

1
..có thể bao gồm assertEquals(first.size(), second.size()).. sau đó nó sẽ hoạt động như mong đợi
chắc chắn là không thể xác định được

17
Điều này không hoạt động với các bản sao trong danh sách. Đây là một ví dụ để chứng minh: 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)); Trong ví dụ này, cả hai xác nhận đều không phát hiện ra rằng các danh sách thực sự khác nhau. @AlexWorden đề cập đến CollectionUtils.isEqualCollection () của Apache Commons Collections, đối với ví dụ này, phát hiện chính xác rằng các bộ sưu tập không bằng nhau.
desilvai

11

Đây là một giải pháp tránh độ phức tạp bậc hai (lặp lại nhiều lần trong danh sách). Điều này sử dụng lớp Apache Commons CollectionUtils để tạo một Bản đồ của từng mục với tần suất đếm chính nó trong danh sách. Sau đó, nó chỉ đơn giản là so sánh hai Bản đồ.

Assert.assertEquals("Verify same metrics series",
    CollectionUtils.getCardinalityMap(expectedSeriesList),
    CollectionUtils.getCardinalityMap(actualSeriesList));

Tôi cũng vừa phát hiện CollectionUtils.isEqualCollection tuyên bố thực hiện chính xác những gì được yêu cầu ở đây ...

https://commons.apache.org/proper/commons-collections/apidocs/index.html?org/apache/commons/collections4/CollectionUtils.html


4

Với AssertJ, containsExactlyInAnyOrder()hoặc containsExactlyInAnyOrderElementsOf()là những gì bạn cần:

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

3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

2

Tôi đến muộn bữa tiệc nhưng đây là giải pháp của tôi chỉ sử dụng Junit. Mọi suy nghĩ đều được hoan nghênh.

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

Lưu ý rằng giải pháp của Roberto Izquierdo nói chung có độ phức tạp bậc hai. Giải pháp trên HashSets luôn có độ phức tạp tuyến tính:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));

2
Cách tiếp cận đó sẽ không hiệu quả. Nếu đầu tiên là ("Chuỗi A") và thứ hai là ("Chuỗi A", "Chuỗi A") thì chúng không phải là danh sách giống nhau.
Alexis C.

4
Bạn không thể kiểm tra kích thước. Nếu đầu tiên là ("s1", "s2", "s3" ,"s1")và thứ hai là ("s2", "s1", "s3" ,"s2");chúng không cùng một danh sách.
Alexis C.

@ZouZou giải pháp được chấp nhận có cùng vấn đề. Bạn đã đề xuất giải pháp thực sự chính xác duy nhất. Nếu bạn đưa ra câu trả lời, tôi sẽ tán thành nó.
leventov

@ZouZou Chúng không giống nhau trong danh sách, nhưng chúng chứa chính xác các Chuỗi giống nhau. OP, làm rõ ?. Ngoài ra, hãy biến nó thành một câu trả lời và tôi cũng sẽ ủng hộ :) không nghĩ về điều đó.
robertoia

2
Điều này vẫn không đúng cho tất cả các trường hợp ("A", "A", "B") sẽ được so sánh bằng ("A", "B", "B")
Tim B

1

Để khắc phục nhanh chóng, tôi sẽ kiểm tra cả hai cách:

assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));

Và thử với một tình huống mà số lượng các phần tử giống nhau khác nhau (ví dụ: 1, 1, 2 và 1, 2, 2) tôi không nhận được kết quả dương tính giả.


1
Mã của bạn vẫn không thành công. Xem ví dụ này - @Test public void test1 () {List <String> list1 = Arrays.asList ("a", "a", "b"); List <Chuỗi> list2 = Arrays.asList ("a", "b", "b"); Assert.assertTrue (list1.containsAll (list2)); Assert.assertTrue (list2.containsAll (list1)); }
Erran Morad

1

Bạn có thể sử dụng ListAssert có trong hũ junit-addons.

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));

0

Có vẻ như các câu trả lời khác hoặc là câu lệnh của bên thứ 3 tham chiếu, không chính xác hoặc không hiệu quả.

Đây là giải pháp vani O (N) trong 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;
}
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.