¿Cuál es la mejor manera de organizar nuestras pruebas unitarias?

18

Hemos acumulado una cantidad sustancial de pruebas unitarias para nuestro programa principal a lo largo de los años. Varios miles. El problema es que no tenemos una idea clara de qué pruebas tenemos porque hay muchas. Y eso es un problema porque no sabemos dónde somos débiles en las pruebas (o dónde tenemos duplicados).

Nuestra aplicación es un motor de informes. Por lo tanto, puede tener una plantilla que se utiliza para probar el análisis (leemos todas las propiedades de la tabla), combinando datos (¿mantuvimos las propiedades correctas de la tabla en la combinación), formateando la página final (es la tabla colocada correctamente en la página ), y / o el formato de salida (es el archivo DOCX creado correcto).

Agregue a esto lo que necesitamos probar. Tome el relleno alrededor de una celda de la tabla (usamos Word, Excel y PowerPoint para el diseño del informe). Tenemos que probar el relleno a través del salto de página, para una tabla dentro de una celda, celdas fusionadas verticalmente, celdas fusionadas horizontalmente, una celda fusionada vertical y horizontalmente que contiene una tabla con celdas fusionadas vertical y horizontalmente en la tabla interna, donde esa tabla se rompe en una página.

Entonces, ¿en qué categoría entra esa prueba? ¿Relleno de tabla, saltos de página, celdas anidadas, celdas fusionadas verticalmente, celdas fusionadas horizontales o algo más?

¿Y cómo documentamos estas categorías, nombramos las pruebas unitarias, etc.?

Actualización: varias personas han sugerido usar herramientas de cobertura para verificar que tengamos cobertura total. Desafortunadamente, esto es de uso limitado en nuestro caso porque los errores tienden a deberse a combinaciones específicas, por lo que es un código que ha sido probado, pero no en esa combinación.

Por ejemplo, ayer tuvimos un cliente que comenzó un marcador de Word al final de un ciclo forEach en su plantilla (un documento de Word) y lo finalizó al comienzo del siguiente ciclo forEach. Todo esto usó código que tiene pruebas unitarias en su contra, pero no habíamos pensado en la combinación de una plantilla que expandía un marcador que comenzaba a iniciarse 25 veces, luego terminaba 10 veces (los dos bucles forEach tenían un número diferente de filas).

David Thielen
fuente
1
Parece que su pregunta es realmente: ¿Cómo sabemos que hemos probado un escenario particular?
Andy Wiesendanger
¡Si! Y también dónde están las pruebas para cualquier escenario similar. Y de eso obtenemos la necesidad # 2: leer lo que está cubierto nos ayuda a encontrar lo que nos hemos perdido.
David Thielen

Respuestas:

13

En general, tiendo a reflejar el árbol de origen para mis pruebas unitarias. Entonces, si tuviera src / lib / fubar, tendría una prueba / lib / fubar que contendría las pruebas unitarias para fubar.

Sin embargo, lo que parece estar describiendo son pruebas más funcionales. En ese caso, tendría una tabla multidimensional que enumerara todas sus posibles condiciones. Entonces, los que no tienen pruebas no tienen sentido o necesitan una nueva prueba. Por supuesto, puede colocarlos en conjuntos de conjuntos de pruebas.

Sardathrion - Restablece a Monica
fuente
Actualmente reflejamos el árbol fuente. Pero tenemos dos problemas. Primero, para el formateo de tablas, hay más de 100 pruebas diferentes. Hacer un seguimiento de lo que se prueba exactamente se ha convertido en un problema. Segundo, áreas funcionales muy diferentes necesitan probar tablas: los analizadores, la sustitución de datos, el formateo y la creación del documento de salida. Así que creo que tienes razón, en cierto sentido es una prueba funcional de una propiedad dada.
David Thielen
Lo que lleva a la pregunta, ¿dónde almacenamos la tabla de pruebas? ¿Estoy pensando en una hoja de cálculo en el directorio fuente raíz?
David Thielen
Lo almacenaría en una hoja de cálculo controlada por versión en el directorio de prueba. Si tiene muchas cosas que necesita probar, sería beneficioso dividirlo en metaestructuras. Intente pensar en términos de qué cosa general se está probando en lugar de qué o cómo.
Sardathrion - Restablece a Monica el
7

La estructura más común parece ser el espejo de directorio srcy test.

src/module/class
test/module/class_test

Sin embargo, hay una estructura alternativa que he visto que ahora creo que es mejor.

src/module/class
src/module/class_test

Dado que las pruebas unitarias tienden a reflejar la clase que están probando, colocarlas en el mismo directorio brinda un acceso mucho más fácil a los archivos para que pueda trabajar en ambos lados.

ming_codes
fuente
2
Un inconveniente del enfoque anterior es que cada vez que decide alterar la estructura de archivos del proyecto, debe hacer lo mismo para la estructura de pruebas. Este problema no existe si las pruebas están donde está el código.
victor175
5

En .NET, tiendo a reflejar, o al menos aproximar, la estructura del espacio de nombres para el código fuente en los proyectos de prueba, debajo de un espacio de nombres maestro "Tests.Unit" o "Tests.Integration". Todas las pruebas unitarias van en un proyecto, con la estructura básica del código fuente replicada como carpetas dentro del proyecto. Lo mismo para las pruebas de integración. Entonces, una solución simple para un proyecto podría verse así:

Solution
   MyProduct.Project1 (Project)
      Folder1 (Folder)
         ClassAA (Class def)
         ...
      Folder2
         ClassAB
         ...
      ClassAC
      ...
   MyProduct.Project2
      Folder1
         ClassBA
         ...
      ClassBB
      ...
   ...
   MyProduct.Tests.Unit
      Project1
         Folder1
            ClassAATests
            ClassAATests2 (possibly a different fixture setup)
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests
      ...
   MyProduct.Tests.Integration
      Project1 (a folder named similarly to the project)
         Folder1 (replicate the folders/namespaces for that project beneath)
            ClassAATests
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests

Para cualquier AAT o AEET que esté codificado con un marco de pruebas unitarias, esto cambia un poco; por lo general, esas pruebas reflejan un conjunto de pasos que probarán la funcionalidad de un nuevo caso de uso o historia. Por lo general, estructuro estas pruebas en un MyProduct.Tests.Acceptanceproyecto como tal, con pruebas para cada historia, posiblemente agrupadas por hito o historia "épica" a la que pertenecía la historia en desarrollo. Sin embargo, esas son realmente solo pruebas de súper integración, por lo que si prefiere estructurar las pruebas de una manera más orientada a objetos en lugar de a historias, ni siquiera necesita un MyProduct.Tests.Acceptanceproyecto similar; simplemente colóquelos MyProduct.Tests.Integrationbajo el alcance del objeto de más alto nivel bajo prueba.

KeithS
fuente
3

No hay razón para que una prueba unitaria sea solo en una categoría. Todas las principales herramientas de prueba Unidad de Apoyo a la creación de conjuntos de pruebas , las cuales vinculan las pruebas para una categoría en particular. Cuando se ha cambiado un área particular de código, el desarrollador debe ejecutar esa suite primero y, a menudo, para ver qué se ha roto. Cuando un acolchado preocupaciones de prueba y se rompe y anidación, por todos los medios la ponen en las tres suites.

Dicho esto, el objetivo de las pruebas unitarias es ejecutarlas todo el tiempo, es decir, deben ser lo suficientemente pequeñas y rápidas para que sea posible ejecutarlas todas antes de cometer cualquier código. En otras palabras, realmente no importa de qué categoría sea una prueba, debe ejecutarse antes de comprometerse de todos modos. Las suites son realmente solo una muleta que usa si por alguna razón no puede escribir pruebas que sean tan rápidas como deberían.

En cuanto a la cobertura, existen muy buenas herramientas de cobertura que le indican qué porcentaje de líneas se ejercieron realmente al ejecutar sus pruebas; este es un indicador obvio de qué tipo de pruebas aún le faltan.

En cuanto a la asignación de nombres, no hay un valor particular en gastar esfuerzo en los nombres de las pruebas unitarias. Todo lo que quiere escuchar de sus exámenes es "5235 de 5235 exámenes aprobados". Cuando una prueba falla, lo que lee no es su nombre, sino el mensaje , por ejemplo, la Cadena en la assert()que implementa su crítica de éxito. El mensaje debe ser lo suficientemente informativo como para tener una idea de lo que está mal sin leer el cuerpo de la prueba. El nombre no es importante en comparación con eso.

Kilian Foth
fuente
De acuerdo al 100% en todo lo que diga (nuestra máquina de compilación ejecuta todas las pruebas en un registro). Nuestro gran problema es rastrear lo que estamos probando. Y la cobertura del código no es de mucha ayuda (vea la actualización más arriba).
David Thielen
1

Una forma de saber si eres débil en las pruebas es la trazabilidad. Por lo general, para las pruebas, esto toma la forma de cobertura.

El objetivo es medir qué partes del código son ejercitadas por sus pruebas, para que pueda ver el código que no está cubierto por sus pruebas. Depende de usted (y la herramienta de cobertura) definir qué es una "parte del código". Lo menos es la cobertura de sucursales.

Mouviciel
fuente