Czy metoda assertEquals w Javie jest niezawodna?

199

Wiem, że ==ma to pewne problemy przy porównywaniu dwóch Strings. Wydaje się, że String.equals()jest to lepsze podejście. Cóż, robię testy JUnit i mam na to ochotę assertEquals(str1, str2). Czy to niezawodny sposób na stwierdzenie, że dwa ciągi zawierają tę samą treść? Użyłbym assertTrue(str1.equals(str2)), ale wtedy nie dostaniesz korzyści, gdy zobaczysz, jakie są oczekiwane i rzeczywiste wartości w przypadku niepowodzenia.

Czy w powiązanej notatce ktoś ma link do strony lub wątku, który wyraźnie wyjaśnia problemy str1 == str2?

DivideByHero
źródło
1
Jeśli nie masz pewności, możesz przeczytać kod lub Javadoc. BTW, jeśli chcesz przetestować, są to ten sam obiekt, którego możesz użyć assertSame.
Peter Lawrey,
2
Jeśli str1 i str2 mają wartość null, assertEquals () ma wartość true, ale assertTrue (str1.equals (str2)) zgłasza wyjątek. Pierwszy przykład wyświetli również przydatny komunikat o błędzie, taki jak zawartość str1 i str2, drugi nie.
Peter Lawrey,

Odpowiedzi:

274

Należy zawsze używać .equals()przy porównywaniu Stringsw Javie.

JUnit wywołuje .equals()metodę w celu ustalenia równości w metodzie assertEquals(Object o1, Object o2).

Jesteś więc zdecydowanie bezpieczny w użyciu assertEquals(string1, string2). (Ponieważ Strings są Objects)

Oto link do wielkiego pytania Stackoverflow dotyczącego niektórych różnic między ==i .equals().

jjnguy
źródło
12
Metoda assertEquals () IIRC kończy się powodzeniem, jeśli oba ciągi są puste. Jeśli nie tego chcesz, wywołaj również assertNotNull ().
finnw
10
dodatkowo, jeśli chcesz przetestować na ==, możesz wywołać assertSame ()
james
7
Nie powiedziałbym zawsze ; czasem pożądana jest równość odniesienia, nawet w przypadku łańcuchów.
Karu
30

assertEqualsużywa equalsmetody do porównania. Istnieje inne stwierdzenie assertSame, które wykorzystuje ==operator.

Aby zrozumieć, dlaczego ==nie należy używać z łańcuchami, musisz zrozumieć, co ==robi: wykonuje kontrolę tożsamości. Oznacza to, że a == bsprawdza, czy ai bodnoszą się do tego samego obiektu . Jest wbudowany w język i jego zachowanie nie może być zmienione przez różne klasy. Z equalsdrugiej strony metoda może być nadpisana przez klasy. Podczas gdy jego domyślnym zachowaniem (w Objectklasie) jest sprawdzanie tożsamości za pomocą ==operatora, wiele klas, w tym String, zastępuje go, aby zamiast tego sprawdzać „równoważność”. W przypadku String, gdyby zamiast sprawdzania ai bodnoszą się do tego samego obiektu,a.equals(b) sprawdza, czy obiekty, do których się odnoszą, są ciągami znaków zawierającymi dokładnie takie same znaki.

Czas analogii: wyobraź sobie, że każdy Stringprzedmiot jest kawałkiem papieru z czymś na nim napisanym. Powiedzmy, że mam na sobie dwa kawałki papieru z napisem „Foo”, a drugi z napisem „Bar”. Jeśli wezmę pierwsze dwa kawałki papieru i ==użyję ich do porównania, wróci, falseponieważ zasadniczo pyta: „czy to ten sam kawałek papieru?”. Nie musi nawet patrzeć na to, co napisano na papierze. Fakt, że daję mu dwa kawałki papieru (zamiast tego samego dwa razy) oznacza, że ​​wróci false. Jeśli equalsjednak użyję, equalsmetoda odczyta dwa kawałki papieru i zobaczy, że mówią to samo („Foo”), więc wróci true.

Bit, który staje się mylący z ciągami, polega na tym, że Java ma koncepcję „internowania” ciągów i jest to (skutecznie) automatycznie wykonywane na literałach ciągów w kodzie. Oznacza to, że jeśli masz w kodzie dwa równoważne ciągi literałów (nawet jeśli należą one do różnych klas), oba będą odnosić się do tego samego Stringobiektu. To sprawia, że ==operator wraca trueczęściej niż można by się spodziewać.

Laurence Gonsalves
źródło
„Oznacza to, że a == b sprawdza, czy aib są tym samym obiektem.” Technicznie sprawdza, czy aib odnoszą się do tego samego obiektu, ponieważ aib są odwołaniami. Chyba że się bardzo mylę.
Bob
@ user1903064, który jest poprawny. Ponieważ zmienne nieprymitywne mogą zawierać tylko odniesienia w Javie, często pomija się dodatkowy poziom pośredni, mówiąc o nich, ale zgadzam się, że w tym przypadku bardziej wyraźne jest korzystne. Zaktualizowałem odpowiedź. Dzieki za sugestie!
Laurence Gonsalves
7

W skrócie - możesz mieć dwa obiekty String, które zawierają te same znaki, ale są różnymi obiektami (w różnych lokalizacjach pamięci). Operator == sprawdza, czy dwa odwołania wskazują na ten sam obiekt (lokalizacja pamięci), ale metoda equals () sprawdza, czy znaki są takie same.

Zwykle chcesz sprawdzić, czy dwa ciągi zawierają te same znaki, a nie czy wskazują one to samo miejsce w pamięci.

Ken Liu
źródło
4
public class StringEqualityTest extends TestCase {
    public void testEquality() throws Exception {
        String a = "abcde";
        String b = new String(a);
        assertTrue(a.equals(b));
        assertFalse(a == b);
        assertEquals(a, b);
    }
}
Carl Manaster
źródło
3

Tak, cały czas jest używany do testowania. Jest bardzo prawdopodobne, że środowisko testowe używa .equals () do takich porównań.

Poniżej znajduje się link wyjaśniający „błąd równości łańcucha”. Zasadniczo łańcuchy w Javie są obiektami, a gdy porównujesz równość obiektów, zwykle są one porównywane na podstawie adresu pamięci, a nie treści. Z tego powodu dwa ciągi nie zajmują tego samego adresu, nawet jeśli ich treść jest identyczna, więc nie będą pasować poprawnie, nawet jeśli wyglądają tak samo po wydrukowaniu.

http://blog.enrii.com/2006/03/15/java-string-equality-common-mistake/

Soviut
źródło
3

JUnit assertEquals(obj1, obj2)rzeczywiście dzwoni obj1.equals(obj2).

Jest też to, assertSame(obj1, obj2)co robi obj1 == obj2(tj. Weryfikuje to obj1i obj2odwołuje się do tego samego wystąpienia), a tego właśnie próbujesz uniknąć.

Nic ci nie jest.

Jack Leow
źródło