Afirmar é igual a 2 listas em junho

164

Como posso fazer uma asserção de igualdade entre listas em um caso de teste JUnit ? A igualdade deve estar entre o conteúdo da lista.

Por exemplo:

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four"); 

// numbers should be equal to numbers2
//numbers should not be equal to numbers3
Kamal
fonte
5
Eu gosto de usar assertArrayEqualshoje em dia. Use em combinação com List#toArray.
Thibstars
@Thibstars - Eu recomendaria isso como resposta.
dfrankow
2
assertArrayEqualsrequer que você obtenha matrizes de suas listas. Você pode operar diretamente na lista usandoassertIterableEquals
ThetaSinner
assertIterableEqualsdisponível para jUnit5 @ThetaSinner
iec2011007

Respostas:

168

Sei que isso foi perguntado há alguns anos, provavelmente esse recurso não existia na época. Mas agora, é fácil fazer isso:

@Test
public void test_array_pass()
{
  List<String> actual = Arrays.asList("fee", "fi", "foe");
  List<String> expected = Arrays.asList("fee", "fi", "foe");

  assertThat(actual, is(expected));
  assertThat(actual, is(not(expected)));
}

Se você possui uma versão recente do Junit instalada com hamcrest, basta adicionar estas importações:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)

http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html

http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html

djeikyb
fonte
3
System.out.println(actual == expected);retornará falso, mas System.out.println(actual.equals(expected));retornará verdadeiro.
Catfish
@ Catfish sim, isso é confuso, não é? Eu acho que estava demonstrando que o matcher está usando em .equals(..)vez de ==?
djeikyb
2
Como isso é melhor do que assertEquals?
quer
1
@Raedwald a saída quando a asserção falha. tentarei voltar mais tarde para editar a diferença. Os correspondentes hamcrest podem gerar mensagens detalhadas de falha. é possível para o junit simplesmente sobrecarregar assertEquals com bondade semelhante. mas principalmente junit fornece recursos essenciais de teste de unidade, e hamcrest fornece uma biblioteca de descritores de diferenças de objetos interessante.
djeikyb
29

Não transforme em string e compare. Isso não é bom para o desempenho. Em junho, dentro do Corematchers, há um matcher para isso =>hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

Esta é a melhor maneira que eu conheço de verificar elementos em uma lista.

Andre Fonseca
fonte
2
Deve ser a resposta escolhida, com uma nota: Você também precisa verificar se não há mais itens na lista além do que deseja. Talvez use:Assert.assertEquals(4,yourList.size());
yoni
ou com uma única linha:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602
3
"Isso não é bom para o desempenho". → Não tenho certeza em que grau se deve levar em consideração o desempenho ao escrever testes de unidade ... Mas é claro que comparar seqüências de caracteres por meio de sua toString()versão não é a melhor maneira.
walen
21

Esta é uma resposta herdada, adequada para o JUnit 4.3 e abaixo. A versão moderna do JUnit inclui mensagens de falha legíveis internas no método assertThat. Prefira outras respostas sobre esta pergunta, se possível.

List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
            "\n  'a'        = "+a+
            "\n  'expected' = "+expected, 
            expected.equals(a));

Para o registro, como @Paul mencionou em seu comentário a esta resposta, dois Lists são iguais:

se e somente se o objeto especificado também for uma lista, ambas as listas terão o mesmo tamanho e todos os pares de elementos correspondentes nas duas listas serão iguais. (Dois elementos e1e e2são iguais se (e1==null ? e2==null : e1.equals(e2)).) Em outras palavras, duas listas são definidas como iguais se contiverem os mesmos elementos na mesma ordem. Essa definição garante que o método equals funcione corretamente em diferentes implementações da Listinterface.

Veja os JavaDocs da Listinterface .

Bart Kiers
fonte
1
Então você quer dizer o esperado.equals (a) cuidará de afirmar os objetos que a lista está segurando?
Kamal
1
Da lista javadoc: Compara o objeto especificado com esta lista para igualdade. Retorna true se e somente se o objeto especificado também for uma lista, ambas as listas têm o mesmo tamanho e todos os pares de elementos correspondentes nas duas listas são iguais. (Dois elementos e1 e e2 são iguais se (e1 == nulo? E2 == nulo: e1.equals (e2)).) Em outras palavras, duas listas são definidas para serem iguais se contiverem os mesmos elementos na mesma ordem . Essa definição garante que o método equals funcione corretamente em diferentes implementações da interface List.
Paul McKenzie
1
Infelizmente, isso fornece uma mensagem de erro menos que útil. Eu achei melhor escrever uma classe de utilitário que executa um loop para que você possa ver quais elementos são diferentes.
Michael Lloyd Lee mlk
@mlk, talvez, mas estou hesitante em escrever um método utilitário personalizado para tal coisa. E a mensagem de erro que editei agora?
Bart Kiers
@mlk Concordo que talvez seja melhor escrever um loop para testar cada elemento, para que você saiba exatamente o que é diferente. Depende de quais tipos de objetos estão na lista. Se forem Strings, diga apenas "a =" + a deve ser bom, mas se forem objetos complexos (outras listas ou algo que não tenha uma boa implementação de toString), será mais fácil testá-los individualmente
Matt N
20

assertEquals(Object, Object)do JUnit4 / JUnit 5 ou assertThat(actual, is(expected));do Hamcrest, proposto nas outras respostas, funcionará apenas como ambos equals()e toString()será substituído pelas classes (e profundamente) dos objetos comparados.

É importante porque o teste de igualdade na asserção se baseia equals()e a mensagem de falha de teste se baseia nos toString()objetos comparados.
Para classes integradas como String, Integere assim por diante ... não há problema, pois essas substituem ambos equals()e toString(). Portanto, é perfeitamente válido afirmar List<String>ou List<Integer>com assertEquals(Object,Object).
E sobre este assunto: você deve substituir equals()em uma classe porque faz sentido em termos de igualdade de objetos, não apenas para facilitar as afirmações em um teste com JUnit.
Para facilitar as afirmações, você tem outras maneiras.
Como prática recomendada, sou a favor das bibliotecas de asserção / correspondência.

Aqui está uma solução AssertJ .

org.assertj.core.api.ListAssert.containsExactly() é o que você precisa: verifica se o grupo real contém exatamente os valores fornecidos e nada mais, na ordem indicada no javadoc.

Suponha uma Fooclasse em que você adicione elementos e onde possa obtê-lo.
Um teste de unidade Foodisso afirma que as duas listas têm o mesmo conteúdo:

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception { 
   Foo foo = new Foo();
   foo.add("One", "Two", "Three");
   Assertions.assertThat(foo.getElements())
             .containsExactly("One", "Two", "Three");
}

Um ponto positivo da AssertJ é que declarar a Listcomo esperado é desnecessário: torna a afirmação mais direta e o código mais legível:

Assertions.assertThat(foo.getElements())
         .containsExactly("One", "Two", "Three");

Mas as bibliotecas de asserções / correspondências são uma obrigação, porque elas realmente serão mais avançadas.
Suponha agora que Foo não armazene Strings mas Bars instâncias.
Essa é uma necessidade muito comum. Com AssertJ a asserção ainda é simples de escrever. Melhor você pode afirmar que o conteúdo da lista é igual, mesmo que a classe dos elementos não substitua equals()/hashCode()enquanto a maneira JUnit exigir que:

import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception {
    Foo foo = new Foo();
    foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
    Assertions.assertThat(foo.getElements())
              .extracting(Bar::getId, Bar::getName)
              .containsExactly(tuple(1, "One"),
                               tuple(2, "Two"),
                               tuple(3, "Three"));
}
davidxxx
fonte
7

Se você não se importa com a ordem dos elementos, recomendo ListAssert.assertEqualsem junit-addons.

Link: http://junit-addons.sourceforge.net/

Para usuários preguiçosos do Maven:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>
Viktor Nordling
fonte
7
Nota: Se você não se importa com a ordem dos elementos, deve usar um Conjunto ou Coleção, não uma Lista.
Barett #
11
Concordo. Essa biblioteca é nojenta. Por que diabos ListAssert.assertEquals () assumiu o padrão sem ordem?
21713 Ryan
5

Você pode usar assertEquals em junho.

import org.junit.Assert;   
import org.junit.Test;

    @Test
    public void test_array_pass()
    {
        List<String> actual = Arrays.asList("fee", "fi", "foe");
        List<String> expected = Arrays.asList("fee", "fi", "foe");
        Assert.assertEquals(actual,expected);
    }

Se a ordem dos elementos for diferente, retornará um erro.

Se você estiver afirmando uma lista de objetos de modelo, substitua o método equals no modelo específico.

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj instanceof ModelName) {
            ModelName other = (ModelName) obj;
            return this.getItem().equals(other.getItem()) ;
        }
        return false;
    }
Dhyan Mohandas
fonte
4

Se você não deseja criar uma lista de matrizes, também pode tentar isso

@Test
public void test_array_pass()
{
  List<String> list = Arrays.asList("fee", "fi", "foe");
  Strint listToString = list.toString();
  Assert.assertTrue(listToString.contains("[fee, fi, foe]"));   // passes  
}
Arun Pratap Singh
fonte
3
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));
Ruslan Gafiullin
fonte
1

Não reinvente a roda!

Existe uma biblioteca do Google Code que faz isso para você: Hamcrest

[Hamcrest] Fornece uma biblioteca de objetos correspondentes (também conhecidos como restrições ou predicados), permitindo que regras de 'correspondência' sejam definidas declarativamente, para serem usadas em outras estruturas. Os cenários típicos incluem estruturas de teste, bibliotecas de simulação e regras de validação da interface do usuário.

Boêmio
fonte
0

Eu sei que já existem muitas opções para resolver esse problema, mas eu prefiro fazer o seguinte para afirmar duas listas em qualquer oder :

assertTrue(result.containsAll(expected) && expected.containsAll(result))
Kh.Taheri
fonte
isso não falhará se o pedido não corresponder?
iec2011007
0

assertEquals(expected, result);funciona para mim. Como essa função obtém dois objetos, você pode passar qualquer coisa para ela.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}
MARCA
fonte
-1

Eu não faço isso, todas as respostas acima estão dando a solução exata para comparar duas listas de objetos. A maioria das abordagens acima pode ser útil no seguinte limite de comparação apenas - Comparação de tamanho - Comparação de referência

Mas se tivermos listas de objetos do mesmo tamanho e dados diferentes no nível dos objetos, essa abordagem de comparação não ajudará.

Eu acho que a abordagem a seguir funcionará perfeitamente com a substituição de igual e método hashcode no objeto definido pelo usuário.

Eu usei a Xstream lib para substituir iguais e código de hash, mas podemos substituir iguais e código de hash por lógicas / comparações vencidas também.

Aqui está o exemplo para sua referência

    import com.thoughtworks.xstream.XStream;

    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.List;

    class TestClass {
      private String name;
      private String id;

      public void setName(String value) {
        this.name = value;
      }

      public String getName() {
        return this.name;
      }

      public String getId() {
        return id;
      }

      public void setId(String id) {
        this.id = id;
      }

      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      @Override
      public boolean equals(Object o) {
        XStream xstream = new XStream();
        String oxml = xstream.toXML(o);
        String myxml = xstream.toXML(this);

        return myxml.equals(oxml);
      }

      /**
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        XStream xstream = new XStream();
        String myxml = xstream.toXML(this);
        return myxml.hashCode();
      }
    }

    public class XstreamCompareTest {
      public static void main(String[] args) throws ParseException {
      checkObjectEquals();
}

      private static void checkObjectEquals() {
        List<TestClass> testList1 = new ArrayList<TestClass>();
        TestClass tObj1 = new TestClass();
        tObj1.setId("test3");
        tObj1.setName("testname3");
        testList1.add(tObj1);

        TestClass tObj2 = new TestClass();
        tObj2.setId("test2");
        tObj2.setName("testname2");
        testList1.add(tObj2);

        testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        List<TestClass> testList2 = new ArrayList<TestClass>();
        TestClass tObj3 = new TestClass();
        tObj3.setId("test3");
        tObj3.setName("testname3");
        testList2.add(tObj3);

        TestClass tObj4 = new TestClass();
        tObj4.setId("test2");
        tObj4.setName("testname2");
        testList2.add(tObj4);

        testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        if (isNotMatch(testList1, testList2)) {
          System.out.println("The list are not matched");
        } else {
          System.out.println("The list are matched");
        }

      }

      private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
        return clist1.size() != clist2.size() || !clist1.equals(clist2);
      }
    }

O mais importante é que você possa ignorar os campos por Anotação (@XStreamOmitField) se não desejar incluir nenhum campo na verificação igual de Objetos. Existem muitas anotações como essa para configurar, portanto, dê uma olhada nas anotações dessa lib.

Estou certo de que esta resposta economizará seu tempo para identificar a abordagem correta para comparar duas listas de objetos :). Por favor, comente se você encontrar algum problema sobre isso.

Ganesa Vijayakumar
fonte