Làm cách nào để khẳng định một Lặp có thể chứa các phần tử với một thuộc tính nhất định?


103

Giả sử tôi muốn kiểm tra đơn vị một phương pháp với chữ ký này:

List<MyItem> getMyItems();

Giả sử MyItemlà một Pojo có nhiều thuộc tính, một trong số đó là "name", được truy cập qua getName().

Tất cả những gì tôi quan tâm khi xác minh là List<MyItem>, hoặc bất kỳ Iterable, chứa hai MyItemphiên bản, mà "name"thuộc tính của chúng có giá trị "foo""bar". Nếu bất kỳ thuộc tính nào khác không khớp, tôi không thực sự quan tâm đến mục đích của bài kiểm tra này. Nếu tên trùng khớp, đó là một thử nghiệm thành công.

Tôi muốn nó là một lớp lót nếu có thể. Đây là một số "cú pháp giả" của loại điều tôi muốn làm.

assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});

Hamcrest có tốt cho loại điều này không? Nếu vậy, chính xác thì phiên bản hamcrest của cú pháp giả của tôi ở trên là gì?

Câu trả lời:


125

Cảm ơn bạn @Razvan đã chỉ cho tôi đúng hướng. Tôi đã có thể lấy nó trong một dòng và tôi đã săn lùng thành công hàng nhập khẩu cho Hamcrest 1.3.

nhập khẩu:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;

mật mã:

assertThat( myClass.getMyItems(), contains(
    hasProperty("name", is("foo")), 
    hasProperty("name", is("bar"))
));

49

Thử:

assertThat(myClass.getMyItems(),
                          hasItem(hasProperty("YourProperty", is("YourValue"))));

2
giống như một nút bên - đây là một giải pháp hamcrest (không phải khẳng địnhj)
Hartmut P.

46

Nó không đặc biệt là Hamcrest, nhưng tôi nghĩ nó đáng được đề cập ở đây. Những gì tôi sử dụng khá thường xuyên trong Java8 là một cái gì đó như:

assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));

(Đã chỉnh sửa để cải thiện một chút của Rodrigo Manyari. Nó ít dài dòng hơn một chút. Xem nhận xét.)

Nó có thể hơi khó đọc hơn một chút, nhưng tôi thích sự an toàn của kiểu và cấu trúc lại. Nó cũng mát mẻ để thử nghiệm nhiều đặc tính của đậu kết hợp. ví dụ với biểu thức && giống java trong lambda bộ lọc.


2
Cải tiến một chút: khẳng địnhTrue (myClass.getMyItems (). Stream (). AnyMatch (item -> "foo" .equals (item.getName ()));
Rodrigo Manyari

@RodrigoManyari, đóng ngoặc mất tích
Abdull

1
Giải pháp này lãng phí khả năng hiển thị thông báo lỗi thích hợp.
Giulio Caccin

@GiulioCaccin Tôi không nghĩ là có. Nếu bạn sử dụng JUnit, bạn có thể / nên sử dụng các phương thức xác nhận đã được nạp chồng và viết confirmTrue (..., "Thông báo lỗi thử nghiệm của riêng tôi"); Xem thêm trên junit.org/junit5/docs/current/api/org/junit/jupiter/api/…
Mario Eis

Ý tôi là, nếu bạn thực hiện khẳng định chống lại Boolean, bạn sẽ mất khả năng in tự động chênh lệch thực tế / dự kiến. Có thể khẳng định bằng cách sử dụng trình so khớp, nhưng bạn cần sửa đổi phản hồi này để tương tự với phản hồi khác trong trang này để thực hiện.
Giulio Caccin

20

Assertj rất giỏi trong việc này.

import static org.assertj.core.api.Assertions.assertThat;

    assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");

Điểm cộng lớn nhất của khẳng định so với hamcrest là dễ dàng sử dụng hoàn thành mã.


16

AssertJ cung cấp một tính năng tuyệt vời extracting(): bạn có thể chuyển Functions để trích xuất các trường. Nó cung cấp một kiểm tra tại thời điểm biên dịch.
Bạn cũng có thể xác nhận kích thước đầu tiên một cách dễ dàng.

Nó sẽ cho:

import static org.assertj.core.api.Assertions;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName)
          .containsExactlyInAnyOrder("foo", "bar"); 

containsExactlyInAnyOrder() khẳng định rằng danh sách chỉ chứa các giá trị này bất kể thứ tự.

Để khẳng định rằng danh sách chứa các giá trị này bất kể thứ tự nhưng cũng có thể chứa các giá trị khác, hãy sử dụng contains():

.contains("foo", "bar"); 

Lưu ý thêm: để xác nhận nhiều trường từ các phần tử của a List, với AssertJ, chúng tôi thực hiện điều đó bằng cách gói các giá trị mong đợi cho mỗi phần tử vào một tuple()hàm:

import static org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName, MyItem::getOtherValue)
          .containsExactlyInAnyOrder(
               tuple("foo", "OtherValueFoo"),
               tuple("bar", "OtherValueBar")
           ); 

4
Đừng hiểu tại sao điều này không có phiếu tán thành. Tôi nghĩ, đây là câu trả lời tốt nhất, cho đến nay.
PeMa

1
Thư viện khẳng định JUnit dễ đọc hơn nhiều so với API xác nhận JUnit.
Sangimed

@Sangimed Đồng ý và tôi cũng thích nó hơn hamcrest.
davidxxx

Theo ý kiến ​​của tôi, điều này hơi khó đọc hơn vì nó tách "giá trị thực tế" ra khỏi "giá trị kỳ vọng" và đặt chúng theo thứ tự cần phải khớp.
Terran

5

Miễn là Danh sách của bạn là một lớp cụ thể, bạn có thể chỉ cần gọi phương thức chứa () miễn là bạn đã triển khai phương thức equals () của mình trên MyItem.

// given 
// some input ... you to complete

// when
List<MyItems> results = service.getMyItems();

// then
assertTrue(results.contains(new MyItem("foo")));
assertTrue(results.contains(new MyItem("bar")));

Giả sử bạn đã triển khai một hàm tạo chấp nhận các giá trị mà bạn muốn xác nhận. Tôi nhận thấy điều này không nằm trên một dòng duy nhất, nhưng sẽ hữu ích khi biết giá trị nào bị thiếu hơn là kiểm tra cả hai cùng một lúc.


1
Tôi thực sự thích giải pháp của bạn, nhưng anh ấy có nên sửa đổi tất cả mã đó để kiểm tra không?
Kevin Bowersox

Tôi nghĩ rằng mọi câu trả lời ở đây sẽ yêu cầu một số thiết lập kiểm tra, thực thi phương thức để kiểm tra và sau đó xác nhận các thuộc tính. Không có chi phí thực sự nào cho câu trả lời của tôi từ những gì tôi có thể thấy, chỉ có điều tôi có hai xác nhận trên đường chia vạch biển để một khẳng định thất bại có thể xác định rõ ràng giá trị nào bị thiếu.
Brad

Tốt nhất là bạn cũng nên bao gồm một thông báo trong khẳng địnhTrue để thông báo lỗi dễ hiểu hơn. Không có thông báo, nếu nó không thành công, JUnit sẽ chỉ ném ra một AssertionFailedError mà không có bất kỳ thông báo lỗi nào. Vì vậy, tốt nhất nên bao gồm một cái gì đó như "kết quả nên chứa MyItem mới (\" foo \ ")".
Tối đa

Có bạn đúng. Tôi khuyên bạn nên hamcrest trong mọi trường hợp, và tôi không bao giờ sử dụng assertTrue () những ngày này
Brad

Để một mặt lưu ý POJO hoặc DTO của bạn nên xác định các bằng phương pháp
Tayab Hussain

1

AssertJ 3.9.1 hỗ trợ việc sử dụng vị từ trực tiếp trong anyMatchphương thức.

assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())

Đây thường là trường hợp sử dụng thích hợp cho tình trạng phức tạp tùy ý.

Đối với các điều kiện đơn giản, tôi thích sử dụng extractingphương pháp (xem ở trên) vì kết quả thử nghiệm có thể lặp lại có thể hỗ trợ xác minh giá trị với khả năng đọc tốt hơn. Ví dụ: nó có thể cung cấp API chuyên biệt như containsphương thức trong câu trả lời của Frank Neblung. Hoặc bạn có thể gọi anyMatchnó sau bằng mọi cách và sử dụng tham chiếu phương thức chẳng hạn như "searchedvalue"::equals. Ngoài ra, nhiều bộ giải nén có thể được đưa vào extractingphương pháp, kết quả sau đó được xác minh bằng cách sử dụng tuple().

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.