2010-05-27 18 views
22

Me pregunto cómo la gente prueba la unidad y afirman que la colección "esperada" es la misma/similar que la colección "real" (el orden no es importante).¿La mejor manera de recopilar la prueba unitaria?

Para realizar esta afirmación, escribí mi API simple aserción: -

public void assertCollection(Collection<?> expectedCollection, Collection<?> actualCollection) { 
    assertNotNull(expectedCollection); 
    assertNotNull(actualCollection); 
    assertEquals(expectedCollection.size(), actualCollection.size()); 
    assertTrue(expectedCollection.containsAll(actualCollection)); 
    assertTrue(actualCollection.containsAll(expectedCollection)); 
} 

Bueno, funciona. Es bastante simple si afirmo solo un montón de enteros o cadenas. También puede ser muy doloroso si intento afirmar una colección de dominios de Hibernate, por ejemplo, por ejemplo. The collection.containsAll (..) depende de los iguales (..) para realizar el control, pero siempre anulo los iguales (..) en mis dominios de Hibernate para verificar solo las claves de negocios (que es la mejor práctica declarada en el Sitio web de Hibernate) y no todos los campos de ese dominio. Claro, tiene sentido comprobar solo en contra de las claves de negocios, pero hay momentos en los que realmente quiero asegurarme de que todos los campos sean correctos, no solo las claves de negocios (por ejemplo, el nuevo registro de entrada de datos). Entonces, en este caso, no puedo perder el control con los Domain.equals (..) y casi parece que necesito implementar algunos comparadores para fines de pruebas unitarias en lugar de confiar en collection.containsAll (..).

¿Hay algunas bibliotecas de prueba que podría aprovechar aquí? ¿Cómo pruebas tu colección?

Gracias.

Respuesta

8

Si el método igual no comprueba todos los campos, puede usar la clase Unitils http://unitils.org/ReflectionAssert. Llamando

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection) 

comparará cada elemento reflexivamente campo por campo (y esto no sólo se aplica para las colecciones, que funcionará para cualquier objeto).

+0

esto parece una buena manera para mi afirmación de colección. Si A amplía B, supongo que tendrá en cuenta los campos de A y B, ¿es correcto? No puedo encontrarlo en la documentación de la API, pero supongo que puedo probarlo. ¿Hay alguna manera de especificar una regla para afirmar solo los campos de A y no de B? – limc

+0

bien, esto es bastante impresionante ... solo lea la documentación, y parece que debería usar ReflectionAssert.assertLenientEquals (..) ya que eso no tiene en cuenta el orden del artículo. Muchas gracias. – limc

+0

@limc. De nada. No estoy seguro de cómo verificar los campos de las subclases (es posible, nunca lo intenté). –

16

No estoy seguro de qué versión de JUnit está utilizando, pero los recientes tienen un método assertThat que toma un Hamcrest Matcher como argumento. Se pueden componer para que pueda construir afirmaciones complejas sobre una colección.

Por ejemplo, si quisiera afirmar que una colección A contenía todos los elementos de la colección B, se podría escribir:

import static org.junit.Assert.*; 
import static org.junit.matchers.JUnitMatchers.*; 
import static org.hamcrest.core.IsCollectionContaining.*; 
import static org.hamcrest.collection.IsCollectionWithSize.*; 
import org.hamcrest.beans.SamePropertyValuesAs; 

public class CollectionTests { 

    /* 
    * Tests that a contains every element in b (using the equals() 
    * method of each element) and that a has the same size as b. 
    */ 
    @Test 
    public void test() { 
     Collection<Foo> a = doSomething(); 
     Collection<Foo> b = expectedAnswer; 

     assertThat(a, both(hasItems(b)).and(hasSize(b.size()))); 
    } 

    /* 
    * Tests that a contains every element in b (using introspection 
    * to compare bean properties) and that a has the same size as b. 
    */ 
    @Test 
    public void testBeans() { 
     Collection<Foo> a = doSomething(); 
     Collection<Foo> b = expectedAnswer; 
     Collection<Matcher<Foo>> bBeanMatchers = 
      new LinkedList<Matcher<Foo>>(); 

     // create a matcher that checks for the property values of each Foo 
     for(Foo foo: B) 
      bBeanMatchers.add(new SamePropertyValuesAs(foo)); 

     assertThat(a, both(hasItems(bBeanMatchers)).and(hasSize(b.size()))) 
    } 
} 

La primera prueba simplemente utiliza el matcher equalTo() de cada objeto (que se delegar en su implementación equals). Si eso no es lo suficientemente fuerte, puede usar el segundo caso, que usará getters y setters para comparar cada elemento. Finalmente, puedes incluso escribir tus propios matchers. El paquete Hamcrest no viene con un emparejador para emparejar por campo (a diferencia de las propiedades de bean coincidentes), pero es trivial escribir un FieldMatcher (y de hecho es un buen ejercicio).

Los Matchers son un poco raros al principio, pero si sigues su ejemplo de hacer que los nuevos Matchers tengan un método estático que devuelva el matcher puedes hacer un montón de import static y tu código básicamente se lee como una frase en inglés (" afirme que a both tiene los artículos en b y tiene el mismo tamaño que b "). Puede construir un DSL bastante impresionante con estas cosas y hacer que su código de prueba sea mucho más elegante.

+0

Gracias por la información. Supongo que nunca me di cuenta de que realmente existen. :) Pero no creo que me funcione en mi situación actual. Acabo de leer la documentación, y parece que equalTo() prueba la igualdad de objetos usando Object.equals, y en mi caso, no quiero jugar con mis iguales (...), si es posible. Pero tendré en cuenta esta útil referencia para uso futuro. – limc

+0

Derecha, el matcher equalTo() usa equals(), pero SamePropertyValuesAs coincide con todos los getters y setters de dos JavaBeans. Sin embargo, si necesita unir campos privados o algo así, tendrá que hacer el suyo si sigue esta ruta. – jasonmp85

+5

@ jasonmp85 ¿Este código aún funciona para usted? Obtengo 'y (org.hamcrest.Matcher >> >) en CombinableMatcher no se puede aplicar a (org.hamcrest.Matcher >>) 'con Hamcrest 1.2 – ArtB

0

No pude obtener la última parte de jasonmp85's answer para que funcione como está. Incluí las importaciones que utilicé porque algunos frascos junit incluyen cosas viejas para mayor comodidad. Esto funciona para mí, pero el ciclo assert definitivamente no es tan bueno como si hasItems(..) funcionara como está escrito en la respuesta de Jason.

import org.hamcrest.Matcher; 
import org.hamcrest.beans.SamePropertyValuesAs; 
import org.hamcrest.collection.IsCollectionWithSize; 

import static org.hamcrest.CoreMatchers.hasItem; 
import static org.hamcrest.MatcherAssert.assertThat; 

... 

/* 
* Tests that a contains every element in b (using introspection 
* to compare bean properties) and that a has the same size as b. 
*/ 
@Test 
public void testBeans() { 
    Collection<Foo> a = doSomething(); 
    Collection<Foo> b = expectedAnswer; 
    Collection<Matcher<Foo>> bBeanMatchers = new LinkedList<Matcher<Foo>>(); 

    // create a matcher that checks for the property values of each Foo 
    for(Foo foo: B) 
     bBeanMatchers.add(new SamePropertyValuesAs(foo)); 

    // check that each matcher matches something in the list 
    for (Matcher<Foo> mf : bBeanMatchers) 
     assertThat(a, hasItem(mf)); 

    // check that list sizes match 
    assertThat(a, IsCollectionWithSize.hasSize(b.size())); 
} 

... 
+0

No necesita los bucles for separados, esto es más conciso: 'para (final Foo expectedFoo: b) {assertThat (a, hasItem (new SamePropertyValuesAs (expectedFoo))); } ' –

1

Otra opción si no se ha de construir su colección:

import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.contains; 
import static org.hamcrest.Matchers.allOf; 
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; 
import static org.hamcrest.Matchers.is; 

@Test 
@SuppressWarnings("unchecked") 
public void test_returnsList(){ 

    arrange(); 

    List<MyBean> myList = act(); 

    assertThat(myList , contains(allOf(hasProperty("id",   is(7L)), 
             hasProperty("name",  is("testName1")), 
             hasProperty("description", is("testDesc1"))), 
           allOf(hasProperty("id",   is(11L)), 
             hasProperty("name",  is("testName2")), 
             hasProperty("description", is("testDesc2"))))); 
} 

Uso containsInAnyOrder si no desea comprobar el orden de los objetos.

P.S. Cualquier ayuda para evitar la advertencia que se suprime será realmente apreciada.

Cuestiones relacionadas