Powiedzmy, że mam klasę bez metody equals (), do której nie ma źródła. Chcę zapewnić równość w dwóch instancjach tej klasy.
Mogę wykonać wiele potwierdzeń:
assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...
Nie podoba mi się to rozwiązanie, ponieważ nie mam pełnego obrazu równości, jeśli wczesne potwierdzenie zawodzi.
Mogę samodzielnie samodzielnie porównać i śledzić wynik:
String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);
Daje mi to pełny obraz równości, ale jest niezgrabny (i nawet nie uwzględniłem możliwych zerowych problemów). Trzecią opcją jest użycie komparatora, ale metoda compareTo () nie powie mi, które pola nie osiągnęły równości.
Czy jest lepsza praktyka, aby uzyskać to, czego chcę od obiektu, bez podklas i nadpisywania równych (ugh)?
java
unit-testing
junit
Ryan Nelson
źródło
źródło
equal
metody mówi tylko, czy dwa wystąpienia są równe i nie obchodzi nas, dlaczego intencje nie są równe.Object
mają swojąequals
metodę, prawdopodobnie miałeś na myśli brak metody zastępowania równych wartości.Odpowiedzi:
Mockito oferuje dopasowanie do refleksji:
Do najnowszej wersji Mockito użyj:
W przypadku starszych wersji użyj:
źródło
org.mockito.internal.matchers.apachecommons
. Stan dokumentacji Mockito:org.mockito.internal
-> "Klasy wewnętrzne, których nie mogą używać klienci." Używając tego, narazisz swój projekt na ryzyko. Może się to zmienić w każdej wersji Mockito. Przeczytaj tutaj: site.mockito.org/mockito/docs/current/overview-summary.htmlMockito.refEq()
zamiast tego.Mockito.refEq()
kończy się niepowodzeniem, gdy obiekty nie mają ustawionego identyfikatora = (refEq
zawodzi do porównania, ponieważ metoda hashcode nie może porównać obiektów.Jest tu wiele poprawnych odpowiedzi, ale chciałbym też dodać moją wersję. Jest to oparte na Assertj.
AKTUALIZACJA: W assertj w wersji 3.13.2 ta metoda jest przestarzała, jak wskazał Woodz w komentarzu. Aktualne zalecenie to
źródło
usingRecursiveComparison()
zisEqualTo()
, tak że wiersz toassertThat(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);
Generalnie implementuję ten przypadek użycia przy użyciu org.apache.commons.lang3.builder.EqualsBuilder
źródło
Wiem, że jest trochę stary, ale mam nadzieję, że pomoże.
Mam ten sam problem, co ty, więc po zbadaniu znalazłem kilka podobnych pytań niż to, a po znalezieniu rozwiązania odpowiadam na te same, ponieważ pomyślałem, że może pomóc innym.
Najczęściej głosowana odpowiedź (nie wybrana przez autora) na to podobne pytanie jest dla Ciebie najbardziej odpowiednim rozwiązaniem.
Zasadniczo polega na wykorzystaniu biblioteki o nazwie Unitils .
To jest zastosowanie:
Co przejdzie, nawet jeśli klasa
User
nie jest implementowanaequals()
. Możesz zobaczyć więcej przykładów i naprawdę fajną asercję nazwanąassertLenientEquals
w ich samouczku .źródło
Unitils
wygląda na to, że nie żyje, zobacz stackoverflow.com/questions/34658067/is-unitils-project-alive .Możesz użyć Apache commons lang ReflectionToStringBuilder
Możesz określić atrybuty, które chcesz przetestować, pojedynczo lub, lepiej, wykluczyć te, których nie chcesz:
Następnie porównujesz te dwa ciągi w normalny sposób. Jeśli chodzi o powolne odbicie, zakładam, że jest to tylko do testowania, więc nie powinno być tak ważne.
źródło
Jeśli używasz hamcrest do swoich potwierdzeń (assertThat) i nie chcesz pobierać dodatkowych bibliotek testowych, możesz użyć
SamePropertyValuesAs.samePropertyValuesAs
do potwierdzenia elementów, które nie mają nadpisanej metody równości.Zaletą jest to, że nie musisz pobierać kolejnego frameworka testowego i da użyteczny błąd, gdy assert zawiedzie (
expected: field=<value> but was field=<something else>
), zamiastexpected: true but was false
używać czegoś podobnegoEqualsBuilder.reflectionEquals()
.Wadą jest to, że jest to płytkie porównanie i nie ma opcji wykluczania pól (tak jak w EqualsBuilder), więc będziesz musiał obejść zagnieżdżone obiekty (np. Usunąć je i porównać niezależnie).
Najlepszy przypadek:
Brzydka sprawa:
Więc wybierz swoją truciznę. Dodatkowe ramy (np. Unitils), niepomocny błąd (np. EqualsBuilder) lub płytkie porównanie (hamcrest).
źródło
Biblioteka Hamcrest 1.3 Utility Matchers ma specjalny element dopasowujący , który wykorzystuje odbicie zamiast równości.
źródło
Niektóre metody porównania odbić są płytkie
Inną opcją jest przekonwertowanie obiektu na json i porównanie ciągów.
źródło
Korzystając z Shazamcrest , możesz:
źródło
reflectEquals
zwraca,false
gdy obiekty mają różne odniesienia.Porównaj pole po polu:
źródło
Asercje AssertJ mogą służyć do porównywania wartości bez
#equals
poprawnie przesłanianej metody, np .:źródło
Ponieważ to pytanie jest stare, zasugeruję inne nowoczesne podejście z wykorzystaniem JUnit 5.
W JUnit 5 istnieje metoda wywoływana,
Assertions.assertAll()
która pozwoli ci zgrupować wszystkie asercje w twoim teście i wykona każde z nich i wyprowadzi wszystkie nieudane asercje na końcu. Oznacza to, że wszelkie asercje, które zawiodą jako pierwsze, nie zatrzymają wykonywania ostatnich twierdzeń.źródło
Możesz użyć refleksji, aby „zautomatyzować” pełne testowanie równości. możesz zaimplementować kod „śledzenia” równości napisany dla pojedynczego pola, a następnie użyć odbicia, aby uruchomić ten test na wszystkich polach w obiekcie.
źródło
Jest to ogólna metoda porównania, która porównuje dwa obiekty tej samej klasy pod względem wartości jej pól (pamiętaj, że te są dostępne za pomocą metody get)
źródło
Natknąłem się na bardzo podobny przypadek.
Chciałem porównać na teście, że obiekt miał te same wartości atrybutów jak innym, ale podobne metody
is()
,refEq()
itp nie będzie działać z powodów podobnych do mojego obiektu o wartości zerowej w swojejid
atrybut.Więc to było rozwiązanie, które znalazłem (cóż, współpracownik znalazł):
Jeśli wartość uzyskana z
reflectionCompare
wynosi 0, oznacza to, że są równe. Jeśli jest -1 lub 1, różnią się one w pewnym atrybucie.źródło
W typowym przypadku AssertJ możesz stworzyć własną strategię porównawczą:
Używanie niestandardowej strategii porównywania w asercjach
Przykłady AssertJ
źródło
Dokładnie tę samą zagadkę napotkałem podczas testowania jednostkowego aplikacji na Androida, a najłatwiejszym rozwiązaniem, które wymyśliłem, było po prostu użycie Gson do konwersji moich obiektów wartości rzeczywistej i oczekiwanej na
json
i porównania ich jako ciągów.Zaletą tego w porównaniu z ręcznym porównywaniem pola po polu jest to, że porównujesz wszystkie pola, więc nawet jeśli później dodasz nowe pole do swojej klasy, zostanie ono automatycznie przetestowane w porównaniu do sytuacji, gdy używałeś wielu pól
assertEquals()
on wszystkie pola, które musiałyby zostać zaktualizowane, jeśli dodasz więcej pól do swojej klasy.jUnit wyświetla również ciągi znaków, dzięki czemu możesz bezpośrednio zobaczyć, gdzie się różnią. Nie jestem jednak pewien, jak niezawodne jest porządkowanie pól
Gson
, może to być potencjalny problem.źródło
Wypróbowałem wszystkie odpowiedzi i nic tak naprawdę nie działało.
Dlatego stworzyłem własną metodę, która porównuje proste obiekty Java bez wchodzenia w zagnieżdżone struktury ...
Metoda zwraca wartość null, jeśli wszystkie pola są zgodne lub ciąg zawiera szczegóły niezgodności.
Porównywane są tylko właściwości, które mają metodę pobierającą.
Jak używać
Przykładowe dane wyjściowe w przypadku niezgodności
Dane wyjściowe pokazują nazwy właściwości i odpowiednie wartości porównywanych obiektów
Kod (Java 8 i nowsze):
źródło
Jako alternatywę dla tylko junit możesz ustawić pola na null przed potwierdzeniem równości:
źródło
Czy możesz umieścić opublikowany kod porównawczy w jakiejś statycznej metodzie narzędziowej?
Niż możesz użyć tej metody w JUnit w następujący sposób:
assertEquals ("Obiekty nie są równe", "", findDifferences (obj1, obj));
który nie jest niezgrabny i daje pełne informacje o różnicach, jeśli one istnieją (poprzez nie do końca w normalnej formie assertEqual, ale otrzymujesz wszystkie informacje, więc powinno być dobrze).
źródło
To nie pomoże OP, ale może pomóc każdemu deweloperowi C #, który skończy tutaj ...
Jak opublikował Enrique , powinieneś zastąpić metodę equals.
Moja sugestia jest taka, aby nie używać podklasy. Użyj częściowej klasy.
Częściowe definicje klas (MSDN)
Twoja klasa wyglądałaby jak ...
W przypadku Javy zgodziłbym się z sugestią użycia refleksji. Pamiętaj tylko, że powinieneś unikać refleksji, kiedy tylko jest to możliwe. Jest powolny, trudny do debugowania, a nawet trudniejszy do utrzymania w przyszłości, ponieważ IDE mogą złamać twój kod, zmieniając nazwę pola lub coś w tym rodzaju. Bądź ostrożny!
źródło
Od twoich komentarzy po inne odpowiedzi, nie rozumiem, czego chcesz.
Na potrzeby dyskusji powiedzmy, że klasa przesłała metodę equals.
Więc twój UT będzie wyglądał mniej więcej tak:
Gotowe. Ponadto nie można uzyskać „pełnego obrazu równości”, jeśli stwierdzenie nie powiedzie się.
Z tego, co rozumiem, mówisz, że nawet gdyby typ zastąpił równe, nie byłbyś nim zainteresowany, ponieważ chcesz uzyskać „pełny obraz równości”. Nie ma więc sensu rozszerzać i zastępować równych sobie.
Masz więc opcje: albo porównaj właściwość po właściwości, używając odbicia lub zakodowanych testów, sugerowałbym to drugie. Lub: porównaj reprezentacje tych obiektów w postaci czytelnej dla człowieka .
Na przykład możesz utworzyć klasę pomocniczą, która serializuje typ, który chcesz porównać z dokumentem XML, a następnie porównać wynikowy XML! w tym przypadku możesz wizualnie zobaczyć, co dokładnie jest równe, a co nie.
Takie podejście daje możliwość spojrzenia na pełny obraz, ale jest również stosunkowo uciążliwe (i początkowo jest mało podatne na błędy).
źródło
Możesz nadpisać metodę equals klasy, na przykład:
Cóż, jeśli chcesz porównać dwa obiekty z klasy, musisz w jakiś sposób zaimplementować metodę equals i metodę hash code
źródło