Pruebas: ¿deterministas o no deterministas?

16

¿Es mejor tener una

  • Conjunto de pruebas deterministas, que da como resultado que las mismas pruebas sean exitosas
  • Conjunto de pruebas no deterministas, que posiblemente cubra más casos

?

Ejemplo: Escribe un conjunto de pruebas para probar la funcionalidad del controlador en una aplicación MVC. El controlador requiere datos de la aplicación de una base de datos como entrada durante la prueba. Hay dos opciones para hacer esto:

  • Usted codifica en qué fila (s) de la base de datos de prueba se seleccionan como entrada (por ejemplo, las filas 10 y 412)
  • Utiliza un generador de números aleatorios para seleccionar de forma pseudoaleatoria los datos de la base de datos (dos filas seleccionadas por un generador de números aleatorios)

El primero es determinista: cada ejecución de la prueba para la misma revisión de código debe producir el mismo resultado. El segundo es no determinista: cada ejecución de la suite de pruebas tiene la posibilidad de producir un resultado diferente. Sin embargo, los datos seleccionados al azar podrían ser una mejor representación de los casos de borde de datos. ¿Podría simular que un usuario alimenta mejor nuestros controladores con datos impredecibles?

¿Cuáles son las razones para elegir una sobre la otra?

    
pregunta DCKing 17.12.2013 - 14:52

4 respuestas

27

Cuando cada ejecución de la suite de prueba le brinda la posibilidad de obtener un resultado diferente, la prueba es casi totalmente inútil: cuando la suite le muestra un error, tiene una alta probabilidad de que no pueda reproducirlo, y cuando lo intente para corregir el error, no puedes probar que tu corrección funciona.

Entonces, cuando piense que necesita usar algún tipo de generador de números aleatorios para generar sus datos de prueba, asegúrese de inicializar siempre ese generador de números aleatorios con la misma semilla, o de guardar sus datos de prueba aleatorios en un archivo antes de alimentar en su prueba, por lo que puede volver a ejecutar la prueba nuevamente con los mismos datos exactos de la ejecución anterior. De esta manera, puede transformar cualquier prueba no determinista en una determinista.

EDITAR: el uso de un generador de números aleatorios para seleccionar algunos datos de prueba es, en mi opinión, un signo demasiado perezoso de elegir datos de prueba buenos . En lugar de tirar 100,000 valores de prueba elegidos al azar y esperar que esto sea suficiente para descubrir todos los errores serios por casualidad, use mejor su cerebro, elija de 10 a 20 casos "interesantes" y utilícelos para el conjunto de pruebas. Esto no solo resultará en una mejor calidad de sus pruebas, sino también en un rendimiento mucho mayor de la suite.

    
respondido por el Doc Brown 17.12.2013 - 15:13
4

Tanto deterministas como no deterministas tienen un lugar

Los dividiría de la siguiente manera:

Pruebas unitarias.

Deben tener pruebas deterministas y repetibles con los mismos datos en todo momento. Las pruebas unitarias acompañan secciones de código aisladas y específicas y deben probarse de manera determinística.

Pruebas de estrés funcionales y de entrada.

Estos pueden usar el enfoque no determinista con las siguientes advertencias:

  • ese hecho está claramente delineado y eliminado
  • los valores aleatorios seleccionados se registran y se pueden volver a intentar manualmente
respondido por el Michael Durrant 12.12.2016 - 15:20
2

Ambos.

Las pruebas deterministas y no deterministas tienen diferentes casos de uso y diferentes valores para su suite. Generalmente no determinista no puede proporcionar la misma precisión que las pruebas deterministas, que poco a poco se ha convertido en "las pruebas no deterministas no proporcionan ningún valor". Esto es falso Pueden ser menos precisos, pero también pueden ser mucho más amplios, lo que tiene sus propios beneficios.

Tomemos un ejemplo: escribes una función que ordena una lista de enteros. ¿Cuáles serían algunas de las pruebas unitarias deterministas que encontrarías útiles?

  • Una lista vacía
  • Una lista con un solo elemento
  • Una lista con todos los mismos elementos
  • Una lista con múltiples elementos únicos
  • Una lista con varios elementos, algunos de los cuales son duplicados
  • Una lista con NaN , INT_MIN y INT_MAX
  • Una lista que ya está parcialmente ordenada
  • Una lista con 10,000,000 elementos

¡Y eso es solo una función de clasificación! Claro, podría argumentar que algunos de estos son innecesarios, o que algunos de estos pueden ser descartados con un razonamiento informal. Pero somos ingenieros y hemos visto el razonamiento informal explotar en nuestra cara. Sabemos que no somos lo suficientemente inteligentes como para comprender completamente los sistemas que hemos construido o mantener la complejidad en nuestras cabezas. Por eso escribimos pruebas en primer lugar. Agregar pruebas no deterministas simplemente dice que no necesariamente seremos lo suficientemente inteligentes como para conocer todas las buenas pruebas a priori. Al incluir datos semi-aleatorios en tu función, es mucho más probable que encuentres un caso límite que te perdiste.

Por supuesto, eso tampoco descarta las pruebas deterministas. Las pruebas no deterministas ayudan a encontrar errores en grandes franjas del programa. Sin embargo, una vez que haya encontrado los errores, necesita una forma reproducible para demostrar que lo ha solucionado. Entonces:

  • Use pruebas no deterministas para encontrar errores en su código.
  • Use pruebas deterministas para verificar correcciones en su código.

Tenga en cuenta que esto significa que muchos consejos sólidos acerca de las pruebas unitarias no se aplican necesariamente a las pruebas no deterministas. Por ejemplo, que deben ser rápidos. Las pruebas de propiedades de bajo nivel deben ser rápidas, pero una prueba no determinista como "simular que un usuario hace clic en los botones de su sitio web y asegurarse de que nunca se produzca un error 500" debería favorecer la exhaustividad sobre la velocidad. Simplemente haga que una prueba como esa se ejecute independientemente de su proceso de compilación para que no ralentice el desarrollo. Por ejemplo, ejecútelo en su propio cuadro de almacenamiento privado.

    
respondido por el Hovercouch 17.12.2016 - 18:20
-1

Realmente no quieres determinista contra no determinista.

Lo que podrías desear es "siempre lo mismo" vs. "no siempre lo mismo".

Por ejemplo, puede tener un número de compilación que aumenta con cada compilación, y cuando desea algunos números aleatorios, inicializa un generador de números aleatorios con el número de compilación como semilla. Por lo tanto, cada compilación hace sus pruebas con diferentes valores, lo que le brinda más oportunidades de encontrar errores.

Pero una vez que se encuentra un error, todo lo que necesita hacer es ejecutar la prueba con el mismo número de compilación, y es reproducible.

    
respondido por el gnasher729 12.12.2016 - 22:28

Lea otras preguntas en las etiquetas