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

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


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.


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 .


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

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>

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

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  
}

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

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.


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

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

-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.

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.