Dlaczego powinienem używać Hamcrest-Matcher i assertThat () zamiast tradycyjnego assertXXX () - Metody

153

Kiedy patrzę na przykłady w klasie Assert JavaDoc

assertThat("Help! Integers don't work", 0, is(1)); // fails:
// failure message:
// Help! Integers don't work
// expected: is <1> 
// got value: <0>
assertThat("Zero is one", 0, is(not(1))) // passes

Dont widzę dużą przewagę nad, powiedzmy assertEquals( 0, 1 ).

Może to fajne dla wiadomości, jeśli konstrukcje stają się bardziej skomplikowane, ale czy widzisz więcej zalet? Czytelność?

Peter Kofler
źródło

Odpowiedzi:

172

Nie ma dużej korzyści w tych przypadkach, w których assertFooistnieje dokładnie zgodne z Twoim zamiarem. W takich przypadkach zachowują się prawie tak samo.

Ale kiedy dojdziesz do nieco bardziej złożonych czeków, korzyść staje się bardziej widoczna:

assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));

vs.

assertThat(foo, hasItems("someValue", "anotherValue"));

Można dyskutować, który z nich jest łatwiejszy do odczytania, ale gdy potwierdzenie się nie powiedzie, otrzymasz dobry komunikat o błędzie assertThat, ale tylko bardzo minimalną ilość informacji assertTrue.

assertThatpowie ci, jakie było twierdzenie i co zamiast tego otrzymałeś. assertTruepowie ci tylko, że dotarłeś falsetam, gdzie się spodziewałeś true.

Joachim Sauer
źródło
To pytanie też tkwiło w mojej głowie. Dziękuję, nigdy nie myślałem o tym w ten sposób.
pszeniczne
1
Pomaga również w „zasadzie” jednego stwierdzenia na test i łatwiej łączy się ze specyfikacjami w stylu BDD.
Nils Wloka
2
I oddziela mechanizm asercji od warunku (co prowadzi do lepszych komunikatów o błędach).
SteveD
2
Przykład jest niewiarygodny, ponieważ mało kto użyłby singla assertTruez rozszerzeniem &&. Rozdzielenie go na dwa warunki sprawia, że ​​przyczyna problemu jest oczywista nawet w JUnit. Nie zrozum mnie źle; Zgadzam się z tobą, po prostu nie lubię twojego przykładu.
maaartinus
48

Informacje o wydaniu JUnit dla wersji 4.4 (tam, gdzie została wprowadzona) podają cztery zalety:

  • Bardziej czytelna i łatwiejsza do wpisania: ta składnia pozwala myśleć w kategoriach podmiotu, czasownika, obiektu (twierdzenie „x to 3”), a nie assertEquals , które używa czasownika, obiektu, podmiotu (twierdzenie „równa się 3 x”)
  • Kombinacje: każda instrukcja dopasowująca może być negowana ( nie (s) ), łączona ( albo (s). Lub (t) ), mapowana do kolekcji ( każda ) lub używana w kombinacjach niestandardowych ( afterFiveSeconds (s) )
  • Czytelne komunikaty o błędach. (...)
  • Dopasowane niestandardowe. Wdrażając samodzielnie interfejs Matcher , możesz uzyskać wszystkie powyższe korzyści dla własnych niestandardowych asercji.

Bardziej szczegółowa argumentacja od gościa, który stworzył nową składnię: tutaj .

Manur
źródło
39

Zasadniczo w celu zwiększenia czytelności kodu .

Poza hamcrest można również użyć fest twierdzeń . Mają kilka zalet w stosunku do ścięgna podkolanowego, takich jak:

Kilka przykładów

import static org.fest.assertions.api.Assertions.*;

// common assertions
assertThat(yoda).isInstanceOf(Jedi.class);
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
assertThat(frodo).isIn(fellowshipOfTheRing);
assertThat(sauron).isNotIn(fellowshipOfTheRing);

// String specific assertions
assertThat(frodo.getName()).startsWith("Fro").endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .excludes(sauron);


// map specific assertions (One ring and elves ring bearers initialized before)
assertThat(ringBearers).hasSize(4)
                       .includes(entry(Ring.oneRing, frodo), entry(Ring.nenya, galadriel))
                       .excludes(entry(Ring.oneRing, aragorn));

Aktualizacja z 17 października 2016 r

Fest nie jest już aktywny, zamiast tego użyj AssertJ .

Igor Popov
źródło
4
Wydaje się, że Fest umarł, ale widelec AssertJ jest bardzo żywy.
Amedee Van Gasse
18

Bardzo podstawowym uzasadnieniem jest to, że trudno jest zepsuć nową składnię.

Załóżmy, że po teście określona wartość foo powinna wynosić 1.

assertEqual(1, foo);

--LUB--

assertThat(foo, is(1));

Przy pierwszym podejściu bardzo łatwo jest zapomnieć o prawidłowej kolejności i wpisać ją od tyłu. Zamiast mówić, że test się nie powiódł, ponieważ oczekiwał 1 i uzyskał 2, komunikat jest odwrócony. Nie stanowi problemu, gdy test zakończy się pomyślnie, ale może prowadzić do nieporozumień, gdy test się nie powiedzie.

W drugiej wersji popełnienie tego błędu jest prawie niemożliwe.

Andy Davis
źródło
... a gdy Eclipse zgłasza błąd asercji, jeśli umieścisz argumenty w niewłaściwej kolejności w tradycyjnej funkcji assertThat (), błąd nie ma sensu.
Sridhar Sarnobat
9

Przykład:

assertThat(5 , allOf(greaterThan(1),lessThan(3)));
//  java.lang.AssertionError:
//  Expected: (a value greater than <1> and a value less than <3>)
//       got: <5>
assertTrue("Number not between 1 and 3!", 1 < 5 && 5 < 3);
//  java.lang.AssertionError: Number not between 1 and 3!
  1. możesz sprecyzować swoje testy
  2. otrzymasz bardziej szczegółowy wyjątek, jeśli testy zakończą się niepowodzeniem
  3. łatwiejszy do odczytania test

btw: możesz też pisać tekst w assertXXX ...

Martin L.
źródło
1
Jeszcze lepiej, zostawiłbym w tym assertThatprzypadku argument typu string , ponieważ wiadomość, którą otrzymujesz automatycznie, jest równie pouczająca: "Oczekiwano: (wartość większa niż <1> i wartość mniejsza niż <3>)"
MatrixFrog
Tak masz rację. Edytuję odpowiedź. Oryginalnie chcę używać zarówno (Matcher). I (Matcher), ale to nie zadziałało.
MartinL
3
assertThat(frodo.getName()).isEqualTo("Frodo");

Jest zbliżony do języka naturalnego.

Łatwiejszy do odczytania, łatwiejszy do analizy kodu. Programista spędza więcej czasu na analizie kodu niż na pisaniu nowego. Jeśli więc kod będzie łatwy do analizy, programista powinien być bardziej produktywny.

Kod PS powinien być równie dobrze napisaną książką. Kod samodokumentowany.

user4515828
źródło
4
Dobrze, i…? Polecam poprzeć swój argument, wyjaśniając, dlaczego to dobrze.
Nathan Tuggy
0

Istnieją zalety do stwierdzenia, że ​​w porównaniu z assertEquals -
1) bardziej czytelne
2) więcej informacji o niepowodzeniach
3) błędy czasu kompilacji - zamiast błędów czasu wykonania
4) elastyczność przy zapisywaniu warunków testowych
5) przenośny - jeśli używasz hamcrest - możesz użyć jUnit lub TestNG jako podstawową strukturę.

samshers
źródło