O método assertEquals do Java é confiável?


199

Eu sei disso == tem alguns problemas ao comparar dois Strings. Parece que String.equals()é uma abordagem melhor. Bem, eu estou fazendo o teste JUnit e minha inclinação é usar assertEquals(str1, str2). Essa é uma maneira confiável de afirmar que duas seqüências contêm o mesmo conteúdo? Eu usaria assertTrue(str1.equals(str2)), mas você não terá o benefício de ver quais são os valores reais e esperados em caso de falha.

Em uma nota relacionada, alguém tem um link para uma página ou tópico que explique claramente os problemas com str1 == str2 ?


1
Se você não tiver certeza, poderá ler o código ou o Javadoc. BTW, se você quiser testar se eles são o mesmo objeto, você pode usar assertSame.
31540 Peter Lawrey

2
Se str1 e str2 forem nulos, assertEquals () é verdadeiro, mas assertTrue (str1.equals (str2)) lança uma exceção. O primeiro exemplo também imprimirá uma mensagem de erro útil, como o conteúdo de str1 e str2, o segundo não.
Peter Lawrey

Respostas:


274

Você sempre deve usar .equals()ao comparar Stringsem Java.

JUnit chama o .equals()método para determinar a igualdade no método assertEquals(Object o1, Object o2).

Então, você está definitivamente seguro de usar assertEquals(string1, string2). (Porque Strings são Objects)

Aqui está um link para uma ótima pergunta sobre o Stackoverflow sobre algumas das diferenças entre ==e .equals().


12
O IIRC assertEquals () será bem-sucedido se as duas strings forem nulas. Se não é isso que você deseja, chame assertNotNull () também.
29125 finnw

10
Além disso, se você quiser teste para ==, você pode chamar assertSame ()
james

7
Eu não diria sempre ; Às vezes, a igualdade de referência é desejada, mesmo para cadeias.
Karu

30

assertEqualsusa o equalsmétodo para comparação. Há uma afirmação diferente assertSame, que usa o ==operador.

Para entender por ==que não deve ser usado com strings, você precisa entender o que ==faz: faz uma verificação de identidade. Ou seja, a == bverifica se ae se brefere ao mesmo objeto . Ele está embutido no idioma e seu comportamento não pode ser alterado por diferentes classes. O equalsmétodo, por outro lado, pode ser substituído por classes. Embora seu comportamento padrão (na Objectclasse) seja fazer uma verificação de identidade usando o ==operador, muitas classes, inclusive String, substituem-na para fazer uma verificação de "equivalência". No caso de String, em vez de verificar se ae se breferir ao mesmo objeto,a.equals(b) verifica se os objetos a que se referem são as duas cadeias que contêm exatamente os mesmos caracteres.

Tempo da analogia: imagine que cada Stringobjeto seja um pedaço de papel com algo escrito nele. Digamos que eu tenha dois pedaços de papel com "Foo" escrito neles e outro com "Bar" escrito. Se eu pegar os dois primeiros pedaços de papel e utilizá ==-los para compará-los, ele retornará falseporque está essencialmente perguntando "estes são o mesmo pedaço de papel?". Nem precisa olhar para o que está escrito no papel. O fato de eu estar entregando dois pedaços de papel (em vez do mesmo duas vezes) significa que ele retornará false. Se eu usar equals, no entanto, o equalsmétodo lerá os dois pedaços de papel e verá que eles dizem a mesma coisa ("Foo"), e assim retornará true.

O pouco que se confunde com Strings é que o Java tem um conceito de "internar" Strings, e isso é (efetivamente) executado automaticamente em qualquer literal de string em seu código. Isso significa que, se você tiver dois literais de string equivalentes em seu código (mesmo que estejam em classes diferentes), eles na verdade se referem ao mesmo Stringobjeto. Isso faz com que o ==operador retorne com truemais frequência do que se poderia esperar.


"Ou seja, a == b verifica se aeb são o mesmo objeto." Tecnicamente, verifica se a e b se referem ao mesmo objeto, já que a e b são referências. A menos que eu esteja muito errado.
bob

@ user1903064 está correto. Como variáveis ​​não primitivas só podem conter referências em Java, é comum pular o nível extra de indireção ao falar sobre elas, mas concordo que, nesse caso, ser mais explícito é benéfico. Eu atualizei a resposta. Obrigado pela sugestão!
Laurence Gonsalves

7

Em poucas palavras - você pode ter dois objetos String que contêm os mesmos caracteres, mas são objetos diferentes (em diferentes locais de memória). O operador == verifica se duas referências estão apontando para o mesmo objeto (local da memória), mas o método equals () verifica se os caracteres são os mesmos.

Normalmente, você está interessado em verificar se duas Strings contêm os mesmos caracteres, não se apontam para o mesmo local de memória.


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);
    }
}

3

Sim, é usado o tempo todo para testes. É muito provável que a estrutura de teste use .equals () para comparações como essas.

Abaixo está um link que explica o "erro de igualdade de string". Essencialmente, as seqüências de caracteres em Java são objetos e, quando você compara a igualdade de objetos, geralmente elas são comparadas com base no endereço da memória e não pelo conteúdo. Por esse motivo, duas cadeias não ocupam o mesmo endereço, mesmo que seu conteúdo seja idêntico; portanto, elas não corresponderão corretamente, mesmo que tenham a mesma aparência quando impressas.

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


3

O JUnit assertEquals(obj1, obj2)realmente chama obj1.equals(obj2).

Há também o assertSame(obj1, obj2)que faz obj1 == obj2(ou seja, verifica isso obj1e obj2faz referência à mesma instância), que é o que você está tentando evitar.

Então você está bem.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.