2012-07-02 9 views
7

Tengo la tarea de crear implementaciones para un gran número de estructuras de datos de métricas (concretamente quadtree y k-d tree variantes). Tengo aproximadamente cuatro de estas implementaciones inactivas, pero la forma en que estoy actualmente probando no es, por mi falta de una mejor palabra, buena.Prueba de unidad Estado interno de la estructura de datos

Necesito una forma limpia de probar la inserción y eliminación de datos de estas estructuras de árbol/trie de forma que pueda probar las estructuras internas de los nodos (verificar padres, hijos, ordenar, etc.). Estas implementaciones siguen pruebas de corrección y análisis de tiempo de ejecución por separado, así que necesito asegurarme de que no solo se inserta un nodo correctamente (es decir, recuperable del árbol más adelante), sino también en una posición muy "correcta" en el árbol.

"Pruebas unitarias" parece ser la forma incorrecta de hacerlo, sin embargo, ya que su propósito, si no me equivoco, es probar una estructura o API externa del sistema. He visto muchas preguntas relacionadas con las pruebas unitarias que preguntaban "¿cómo obtengo acceso a un campo privado en la prueba de unidad" o "cómo pruebo los valores de devolución de un método no público?", Y la respuesta generalmente es "no" t "- y estoy de acuerdo con esta respuesta.

Y por lo que no deja a nadie dispuesto a ayudar con divagaciones simplemente vagos, la interfaz de mis árboles implementan es la siguiente (con sede fuera interfaz de mapa de la colección de Java):

public interface SpatialMap<K, V> extends Iterable<SpatialMap.Entry<K, V>> 
{ 
// Query Operations 

/** 
* Returns the number of key-value mappings in this map. If the map contains more than 
* <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>. 
* 
* @return The number of key-value mappings in this map. 
*/ 
int size(); 

/** 
* Returns <tt>true</tt> if this map contains no key-value mappings. 
* 
* @return <tt>true</tt> if this map contains no key-value mappings. 
*/ 
boolean isEmpty(); 

/** 
* Returns <tt>true</tt> if this map contains a mapping for the specified key. 
* 
* @param key 
*   The key whose presence in this map is to be tested. 
* @return <tt>true</tt> if this map contains a mapping for the specified key. 
*/ 
boolean containsKey(K key); 

/** 
* Returns the value to which the specified key is mapped, or {@code null} if this map contains 
* no mapping for the key. 
* 
* <p>A return value of {@code null} does not <i>necessarily</i> indicate that the map contains 
* no mapping for the key; it's also possible that the map explicitly maps the key to 
* {@code null}. The {@link #containsKey containsKey} operation may be used to distinguish these 
* two cases. 
* 
* @see #put(Comparable, Comparable, Object) 
* 
* @param key 
*   The key whose associated value is to be returned. 
* @return The value to which the specified key is mapped, or {@code null} if this map contains 
*   no mapping for the key. 
*/ 
V get(K key); 

// Modification Operations 

/** 
* Associates the specified value with the specified key in this map. If the map previously 
* contained a mapping for the key, the old value is replaced. 
* 
* @param key 
*   The key with which the specified value is to be associated. 
* @param data 
*   The value to be associated with the specified key. 
* @return The previous value associated with the key, or <tt>null</tt> if there was no mapping 
*   for the key. (A <tt>null</tt> return can also indicate that the map previously 
*   associated <tt>null</tt> with <tt>key</tt>.) 
*/ 
V put(K key, V data); 

/** 
* Removes the mapping for the specified key from this map if present. 
* 
* @param key 
*   The key whose mapping is to be removed from the map. 
* @return The previous value associated with the key, or <tt>null</tt> if there was no mapping 
*   for the key. (A <tt>null</tt> return can also indicate that the map previously 
*   associated <tt>null</tt> with <tt>key</tt>.) 
*/ 
V remove(K key); 

// Bulk Operations 

/** 
* Removes all of the mappings from this map. The map will be empty after this call returns. 
*/ 
void clear(); 
} 

Esto hace que sea difícil Pruebe con solo los métodos públicos, ya que necesito ciertos datos (punteros para niños/padres) que no están disponibles en la interfaz pública. Además, en las estructuras trie (PR Quadtree, PRKDTree, variantes de MX, etc.) tengo nodos que están separados de los datos, por lo que la creación de un método público que devuelva un "nodo" también se abstraerá demasiado para obtener los datos correctos.

¿Qué tipo de método de prueba (o técnicas que podría usar con JUnit y no siento que estoy destruyendo hermosos límites cognitivos) estoy buscando?

Respuesta

5

Existen casos como este en los que a veces realmente se necesita probar el estado interno de una estructura. En este caso, accedería a las variables internas utilizando la reflexión. Hay algunos complementos JUnit (PrivateAccessor http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html) que hacen esto más fácil.

La compensación es que su prueba será más frágil porque si el estado interno cambia, entonces su prueba puede romperse. Pero si desea tener la confianza de que su estado interno es correcto, a veces debe hacer esto.

+0

El estado interno en cuanto a ordenamientos de nodos nunca debe cambiar, siempre que mis pruebas de corrección sean realmente correctas;) – efritz

+2

Correcto, pero si cambian los nombres de las variables, por ejemplo, la prueba puede romperse. Esto es más una prueba de unidad de whitebox (a diferencia de blackbox).Algunas veces este estilo es apropiado, y suena como en tu caso, es lo que estás buscando. –

1

Un método que he usado en este tipo de situación es proteger esos campos internos y crear una subclase para probar. A través de esa subclase, puede exponer cualquier estado que sea necesario para su prueba de caja blanca.

0

Si coloca la interfaz y sus implementaciones solo en un paquete dedicado, y crea los métodos de estado interno de esa implementación protegida por paquete, entonces sus pruebas pueden acceder a ellos y potencialmente probarlos, mientras que el el resto de tu sistema no puede.

No es bueno para los "puristas" de pruebas unitarias, pero así es como lo hago, cuando no quiero exponer las agallas de mi clase al resto del sistema, pero aún quiero hacer afirmaciones en su comportamiento interno.

Cuestiones relacionadas