¿Todas las pruebas unitarias en un ejecutable, o dividirlas?

12

Al escribir pruebas para una pieza de software, por ejemplo, una biblioteca, ¿prefiere compilar todas las pruebas unitarias en una o separarlas en varios ejecutables?

La razón por la que pregunto es porque actualmente estoy usando CUnit para probar una biblioteca en la que estoy trabajando. Las pruebas se dividen en suites separadas que se compilan en un ejecutable completo con salida impresa para fallas. Ahora, el sistema de compilación para esa biblioteca es CMake (que, a pesar de su nombre, tiene poco que ver con CUnit), que viene con su propio marco de prueba, CTest . CTest me permite registrar una lista de ejecutables que sirven como pruebas.

Estoy pensando si usar CTest para las pruebas automatizadas. Sin embargo, esto requeriría que divida las pruebas que he escrito hasta ahora en objetivos de compilación separados. De lo contrario, no puedo utilizar algunas de las funciones avanzadas de CTests, como la ejecución selectiva de pruebas.

Me doy cuenta de que esto es más una cuestión de qué herramientas usar y su manejo y convenciones, pero aparte de eso, ¿hay alguna otra razón para preferir un único ejecutable de prueba en lugar de otros? ¿O viceversa?

Benjamin Kloster
fuente
Separarlos en ejecutables separados por clase. Cada clase debe tener su propia prueba de unidad, a menos que la clase no sea comprobable por unidad y, por lo tanto, deba ser probada indirectamente por otras clases.
Brian
1
Si tiene una biblioteca grande con cientos de clases, cada una con una prueba unitaria, el tiempo de construcción es mucho más largo si crea un binario completo para cada uno, en comparación con uno (o algunos) grandes binarios. Además, son muchos archivos MAKE para administrar, cada uno con líneas de enlace individuales.
JBRWilkinson
No es tan grande. Menos de 20 módulos. También puedo compilarlos todos con los mismos indicadores, por lo que CMake puede generar los Makefiles sin mucho trabajo de mi parte.
Benjamin Kloster

Respuestas:

5

Me gusta tener mis pruebas automatizadas en archivos binarios individuales, o al menos agrupados por grupo "pertenece juntos", y luego llamarlos desde un simple script de shell (donde un código de salida distinto de cero indica un fallo, y la salida en stderr puede ser capturada para grabar una explicación). De esta manera, conservo total flexibilidad en las pruebas: puedo ejecutar pruebas individuales directamente desde la línea de comandos, puedo hacer todo tipo de scripts sofisticados si lo deseo, puedo reordenarlos como mejor me parezca sin volver a compilar nada, etc.

Pero lo más importante, también me permite incluir pruebas escritas en diferentes idiomas o usar diferentes cadenas de herramientas en la misma ejecución. Por ejemplo, las pruebas unitarias que escribo son muy probablemente en el idioma principal del proyecto, y ejecutarlas es una cuestión de construir e invocar los binarios; pero también quiero probar mi base de datos, y es posible que desee alimentar scripts SQL directamente a la base de datos para esto; Es posible que desee ejecutar alguna herramienta de análisis de código estático en mi código (incluso si es solo una especie de linter). Es posible que desee ejecutar mi HTML estático a través de un verificador de validez. Podría ejecutar un grepcomando sobre la base de código para verificar construcciones sospechosas, violaciones de estilo de codificación o palabras clave de "bandera roja". Las posibilidades son infinitas: si se puede ejecutar desde la línea de comandos y se adhiere a "el estado de salida cero significa OK", puedo usarlo.

tdammers
fuente
El argumento agnóstico del lenguaje es un muy buen punto, ya que planeo implementar enlaces de Python para la biblioteca en el futuro. ¡Gracias!
Benjamin Kloster
@tdammers: ¿algún marco de prueba en particular?
JBRWilkinson
@JBRWilkinson: solo un script de shell de 30 líneas. Para la mayoría de los lenguajes que uso, tengo pequeñas bibliotecas para tareas de prueba comunes, como ejecutar una función, comparar el resultado con un valor esperado y tirar cuando no coinciden. Pero cualquier marco de prueba de unidad dado podría integrarse fácilmente en un "meta-sistema", siempre que pueda ejecutarse desde la línea de comando y señale el éxito / fracaso a través de su estado de salida.
tdammers
2

Tiendo a tener una biblioteca para las pruebas unitarias de una aplicación (o para un paquete de bibliotecas que comúnmente se comparte). Dentro de esa biblioteca, trato de replicar o aproximar los espacios de nombres de los objetos bajo prueba para los accesorios de prueba (uso NUnit principalmente). Esto simplifica la compilación, ya que en .NET hay una sobrecarga inherente en la construcción de cada binario que aumentaría el tiempo de construcción de una solución de 20 proyectos sobre el de una solución de 10 proyectos con el mismo LOC. Los binarios de prueba no se distribuyen de todos modos, por lo que cualquier organización de las pruebas en binarios es para su propia conveniencia, y generalmente encuentro que YAGNI se aplica aquí como en cualquier lugar.

Ahora, generalmente no tengo las consideraciones que tiene tdammers; mi código está prácticamente en un solo idioma, y ​​cualquier prueba que involucre cadenas SQL no es una prueba unitaria (a menos que esté probando que un productor de consultas devuelve la cadena SQL esperada dados ciertos criterios), y prácticamente nunca hago una prueba unitaria del valor real IU (en muchas situaciones es simplemente imposible). También utilizo una biblioteca de pruebas unitarias que es bien aceptada por herramientas de terceros, como build-bots y complementos IDE, por lo que cualquier preocupación por ejecutar pruebas individuales, suites parciales, etc. es mínima.

KeithS
fuente
1
Una cuestión de cultura, supongo, en un entorno .NET, probablemente razonaría como tú.
tdammers