Yinelenebilir bir özelliğin belirli bir özelliğe sahip öğeler içerdiğini nasıl iddia edebilirim?


103

Bu imzayla bir yöntemi test etmek istediğimi varsayalım:

List<MyItem> getMyItems();

Farz edelim MyItemki, biri "name"aracılığıyla erişilen birçok özelliği olan bir Pojo getName().

Doğrulama hakkında tek şey bakımı olmasıdır List<MyItem>, ya da herhangi bir Iterable, iki içeriyor MyItemolan örneklerini, "name"özellikleri değerlere sahip "foo"ve "bar". Diğer özellikler eşleşmezse, bu testin amaçlarını gerçekten umursamıyorum. İsimler eşleşirse, başarılı bir testtir.

Mümkünse tek satırlık olmasını isterim. İşte yapmak istediğim türden bazı "sözde sözdizimi".

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

Hamcrest bu tür şeyler için iyi olur mu? Öyleyse, yukarıdaki sözdizimimin hamcrest versiyonu tam olarak ne olurdu?

Yanıtlar:


125

Beni doğru yönlendiren @Razvan teşekkür ederim. Onu tek satırda alabildim ve Hamcrest 1.3 ithalatını başarıyla yakaladım.

ithalatlar:

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;

kod:

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

49

Deneyin:

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

2
tıpkı bir yan düğüm olarak - bu bir hamcrest çözümüdür (assertj değil)
Hartmut S.

46

Özellikle Hamcrest değil, ama burada bahsetmeye değer olduğunu düşünüyorum. Java8'de oldukça sık kullandığım şey şuna benzer:

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

(Rodrigo Manyari'nin ufak iyileştirmesiyle düzenlendi. Biraz daha az ayrıntılı. Yorumlara bakın.)

Okuması biraz daha zor olabilir, ancak türü ve yeniden düzenleme güvenliğini seviyorum. Aynı zamanda birden fazla çekirdek özelliğini kombinasyon halinde test etmek için de harikadır. örneğin, filtre lambda'sında java benzeri bir && ifadesi ile.


2
Küçük bir gelişme: assertTrue (myClass.getMyItems (). Stream (). AnyMatch (item -> "foo" .equals (item.getName ()));
Rodrigo Manyari

@RodrigoManyari, kapanış parantezi eksik
Abdull

1
Bu çözüm, uygun bir hata mesajı gösterme olasılığını boşa çıkarır.
Giulio Caccin

@GiulioCaccin Öyle olduğunu sanmıyorum. JUnit kullanıyorsanız, aşırı yüklenmiş onaylama yöntemlerini kullanabilir ve assertTrue yazabilirsiniz (..., "Kendi test hatası mesajım"); Daha bakın junit.org/junit5/docs/current/api/org/junit/jupiter/api/...
Mario eis

Demek istediğim, bir Boolean'a karşı iddiayı yaparsanız, gerçek / beklenen farkı otomatik olarak yazdırma yeteneğini kaybedersiniz. Bir eşleştirici kullanarak iddia etmek mümkündür, ancak bunu yapmak için bu yanıtı bu sayfadaki diğerine benzer olacak şekilde değiştirmeniz gerekir.
Giulio Caccin

20

Assertj bunda iyidir.

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

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

Hamcrest'e kıyasla assertj için büyük bir artı, kod tamamlamanın kolay kullanımıdır.


16

AssertJ mükemmel bir özellik sağlar extracting(): Functionalanları ayıklamak için s'yi geçebilirsiniz . Derleme zamanında bir kontrol sağlar.
Ayrıca önce boyutu kolayca belirleyebilirsin.

Verecek:

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

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

containsExactlyInAnyOrder() sırasına bakılmaksızın listenin yalnızca bu değerleri içerdiğini iddia eder.

Listenin sıralaması ne olursa olsun bu değerleri içerdiğini ancak diğer değerleri de içerebileceğini iddia etmek için şunu kullanın contains():

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

Bir yan not olarak: a öğesinin öğelerinden birden çok alanı ileri sürmek için List, AssertJ ile bunu her öğe için beklenen değerleri bir tuple()işleve sararak yaparız :

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
Bunun neden olumlu oyları olmadığını anlamayın. Bence bu açık ara en iyi cevap.
PeMa

1
AssertJ kütüphanesi, JUnit assertion API'den çok daha okunabilir.
2018

@Sangimed Kabul etti ve ben de hamcrest'i tercih ediyorum.
davidxxx

Bana göre bu, "gerçek değeri" "beklenen değerden" ayırdığı ve bunları eşleşmesi gereken bir sıraya koyduğu için biraz daha az okunabilir.
Terran

5

Listeniz somut bir sınıf olduğu sürece, MyItem üzerinde equals () yönteminizi uyguladığınız sürece basitçe contains () yöntemini çağırabilirsiniz.

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

Üzerinde iddia etmek istediğiniz değerleri kabul eden bir kurucu uyguladığınızı varsayar. Bunun tek bir satırda olmadığının farkındayım, ancak ikisini birden kontrol etmektense hangi değerin eksik olduğunu bilmek yararlıdır.


1
Çözümünüzü gerçekten beğendim, ancak bir test için tüm bu kodu değiştirmeli mi?
Kevin Bowersox

Buradaki her cevabın bazı test kurulumlarını, test etmek için yöntemin yürütülmesini ve ardından özellikleri ileri sürmeyi gerektireceğini düşünüyorum. Görebildiğimden cevabımın gerçek bir yükü yok, sadece deniz hattı hatlarında iki iddiam var, böylece başarısız bir iddia hangi değerin eksik olduğunu açıkça belirleyebilir.
Brad

Hata mesajının daha anlaşılır olması için assertTrue içine bir mesaj eklemek en iyisidir. Bir mesaj olmadan başarısız olursa, JUnit herhangi bir hata mesajı olmadan bir AssertionFailedError atacaktır. Bu nedenle, "sonuçlar yeni MyItem (\" foo \ ") içermelidir" gibi bir şey eklemek en iyisidir.
Maksimum

Evet haklısın. Her halükarda Hamcrest'i tavsiye ederim ve bu günlerde asla assertTrue () kullanmıyorum
Brad

Bir yandan, POJO veya
DTO'nuzun

1

AssertJ 3.9.1, anyMatchyöntemde doğrudan yüklem kullanımını destekler .

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

Bu genellikle keyfi olarak karmaşık koşullar için uygun kullanım durumudur.

Basit koşullar için extractingyöntemi kullanmayı tercih ederim (yukarıya bakın) çünkü sonuçta yinelenebilir test altında daha iyi okunabilirlikle değer doğrulamasını destekleyebilir. Örnek: containsFrank Neblung'un cevabındaki yöntem gibi özelleştirilmiş API sağlayabilir . Ya anyMatchda daha sonra yine de arayabilir ve gibi yöntem referansını kullanabilirsiniz "searchedvalue"::equals. Ayrıca birden fazla ekstraktör extractingyönteme konulabilir , sonuçlar daha sonra kullanılarak doğrulanabilir tuple().

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.