Considere un método para barajar elementos aleatoriamente en una matriz. ¿Cómo escribiría una prueba unitaria simple pero robusta para asegurarse de que esto funciona?
Se me ocurrieron dos ideas, las cuales tienen defectos notables:
- Mezcle la matriz y luego asegúrese de que su orden sea diferente al anterior. Esto suena bien, pero falla si el aleatorio se baraja en el mismo orden. (Improbable, pero posible).
- Mezcle la matriz con una semilla constante y compárela con la salida predeterminada. Esto se basa en la función aleatoria que siempre devuelve los mismos valores dados la misma semilla. Sin embargo, esto a veces es una suposición no válida .
Considere una segunda función que simula tiradas de dados y devuelve un número aleatorio. ¿Cómo probarías esta función? ¿Cómo probarías que la función ...
- nunca devuelve un número fuera de los límites dados?
- devuelve números en una distribución válida? (Uniforme para un dado, normal para grandes cantidades de dados).
Estoy buscando respuestas que ofrezcan información para probar no solo estos ejemplos, sino elementos aleatorios de código en general. ¿Son las pruebas unitarias incluso la solución correcta aquí? Si no, ¿qué tipo de pruebas son?
Sólo para aliviar la mente de todos que estoy no escribir mi propio generador de números aleatorios.
testing
unit-testing
random
dlras2
fuente
fuente
Respuestas:
No creo que las pruebas unitarias sean la herramienta adecuada para probar la aleatoriedad. Una prueba unitaria debe llamar a un método y probar el valor devuelto (o el estado del objeto) contra un valor esperado. El problema con la prueba de aleatoriedad es que no hay un valor esperado para la mayoría de las cosas que le gustaría probar. Puede probar con una semilla determinada, pero eso solo prueba la repetibilidad . No le da ninguna forma de medir qué tan aleatoria es la distribución, o si es siquiera aleatoria.
Afortunadamente, hay muchas pruebas estadísticas que puedes ejecutar, como la batería de pruebas de aleatoriedad Diehard . Ver también:
¿Cómo probar un generador de números pseudoaleatorios?
Prueba unitaria con funciones que devuelven resultados aleatorios
Unit Testing Randomness es un artículo de wiki que habla sobre muchos de los desafíos que ya se mencionaron al tratar de probar lo que, por su naturaleza, no es repetible. Una parte interesante que deduje fue la siguiente:
fuente
1. Unidad prueba tu algoritmo
Para la primera pregunta, construiría una clase falsa en la que alimentaría una secuencia de números aleatorios para la cual conoce el resultado de su algoritmo. De esa manera, se asegura de que el algoritmo que construye sobre su función aleatoria funcione. Entonces algo en la línea de:
2. Vea si su función aleatoria tiene sentido
A la prueba unitaria, debe agregar una prueba que se ejecute varias veces y afirme que los resultados
2
aumento entre el 10% y el 20% (1/6 = 16.67%) del tiempo dado que lo rodaste 1000 veces).3. Prueba de integración para el algoritmo y la función aleatoria.
¿Con qué frecuencia esperaría que su matriz se clasifique en la clasificación original? Ordene un par de cientos de veces y afirme que solo el x% de las veces la clasificación no cambia.
Esto ya es una prueba de integración, está probando el algoritmo junto con la función aleatoria. Una vez que está utilizando la función aleatoria real, ya no puede salirse con una sola prueba.
Por experiencia (escribí un algoritmo genético), diría que combinar la prueba unitaria de su algoritmo, la prueba de distribución de su función aleatoria y la prueba de integración es el camino a seguir.
fuente
Un aspecto de los PRNG que parece olvidado es que todas sus propiedades son de naturaleza estadística: no se puede esperar que mezclar una matriz resulte en una permutación diferente de la que comenzó. Básicamente, si está utilizando un PRNG normal, lo único que tiene garantizado es que no utiliza un patrón simple (con suerte) y que incluso tiene una distribución entre el conjunto de números que devuelve.
Una prueba adecuada para un PRNG implicará ejecutarlo al menos 100 veces y luego verificar la distribución de la salida (que es una respuesta directa a la segunda parte de la pregunta).
Una respuesta a la primera pregunta es casi la misma: ejecute la prueba unas 100 veces con {1, 2, ..., n} y cuente la cantidad de veces que cada elemento ha estado en cada posición. Deben ser todos aproximadamente iguales si el método aleatorio es bueno.
Una cuestión completamente diferente es cómo probar PRNG de grado criptográfico. Este es un asunto en el que probablemente no debería detenerse, a menos que realmente sepa lo que está haciendo. Se sabe que las personas destruyen (léase: abren agujeros catastróficos) buenos criptosistemas con solo unas pocas 'optimizaciones' o ediciones triviales.
EDITAR: He releído completamente la pregunta, la respuesta principal y la mía. Si bien los puntos que planteo siguen en pie, respaldaría la respuesta de Bill The Lizard. Las pruebas unitarias son de naturaleza booleana: fallan o tienen éxito y, por lo tanto, no son adecuadas para probar "cuán buenas" son las propiedades de un PRNG (o un método que usa un PRNG), ya que cualquier respuesta a esta pregunta sería cuantitativa , en lugar de polar.
fuente
Hay dos partes en esto: probar la aleatorización y probar cosas que usan la aleatorización.
Probar la aleatorización es relativamente sencillo. Comprueba que el período del generador de números aleatorios es el que espera que sea (para algunas muestras que utilizan algunas semillas un poco aleatorias, dentro de cierto umbral) y que la distribución de la salida en un tamaño de muestra grande es la esperada que sea (dentro de cierto umbral).
Probar cosas que usan aleatorización se realiza mejor con un generador de números psuedo-aleatorio determinista. Dado que el resultado de la aleatorización se conoce en función de la semilla (sus entradas), puede realizar pruebas unitarias de forma normal en función de las entradas frente a las salidas esperadas. Si su RNG no es determinista, simule con uno que sea determinista (o simplemente no aleatorio). Pruebe la aleatorización de forma aislada del código que lo consume.
fuente
Deje que se ejecute varias veces y visualice sus datos .
Aquí hay un ejemplo de un shuffle de Coding Horror , puedes ver que el algoritmo está bien o no:
Es fácil ver que cada elemento posible se devuelve al menos una vez (los límites están bien) y que la distribución está bien.
fuente
Punteros generales que he encontrado útiles cuando se trata de código que toma entradas aleatorias: Verifique los casos límite de aleatoriedad esperada (valores máximo y mínimo, y los valores máximo + 1 y min-1 si corresponde). Verifique los lugares (en, arriba y abajo) donde los números tienen puntos de inflexión (es decir, -1, 0, 1 o mayor que 1, menor que 1 y no negativo para los casos en los que un valor fraccional podría estropear la función). Verifique algunos lugares completamente fuera de la entrada permitida. Verifique algunos casos típicos. También puede agregar una entrada aleatoria, pero para una prueba unitaria que tiene el efecto secundario indeseable de que el mismo valor no está bajo prueba cada vez que se ejecuta la prueba (un enfoque de semilla puede funcionar, pruebe los primeros 1,000 números aleatorios de semilla S o somesuch).
Para probar el resultado de una función aleatoria, es importante identificar el objetivo. En el caso de las cartas, ¿el objetivo es probar la uniformidad del generador aleatorio 0-1, para determinar si las 52 cartas aparecen en el resultado, o algún otro objetivo (tal vez toda esta lista y más)?
En el ejemplo específico, debe suponer que su generador de números aleatorios es opaco (al igual que no tiene sentido probar la syscall o malloc- del sistema operativo a menos que escriba sistemas operativos). Puede ser útil medir el generador de números aleatorios, pero su objetivo no es escribir un generador aleatorio, solo para ver que obtiene 52 tarjetas cada vez, y que cambian de orden.
Esa es una forma larga de decir que realmente hay dos tareas de prueba aquí: probar que el RNG está produciendo la distribución correcta y verificar que el código aleatorio de su tarjeta esté usando ese RNG para producir resultados aleatorios. Si está escribiendo el RNG, use un análisis estadístico para probar su distribución, si está escribiendo el barajador de tarjetas, asegúrese de que haya 52 tarjetas no repetidas en cada salida (es un mejor caso de prueba por inspección que está usando El RNG).
fuente
Puede confiar en generadores seguros de números aleatorios
Tuve un pensamiento horrible: no estás escribiendo tu propio generador de números aleatorios, ¿verdad?
Suponiendo que no lo eres, entonces debes probar el código del que eres responsable , no el código de otras personas (como la
SecureRandom
implementación de tu marco).Probar tu código
Para comprobar que su código responde correctamente, es normal utilizar un método de baja visibilidad para producir números aleatorios para que una clase de prueba unitaria pueda anularlo fácilmente. Este método anulado se burla efectivamente del generador de números aleatorios y le brinda un control completo sobre lo que se produce y cuándo. En consecuencia, puede ejercer completamente su código, que es el objetivo de las pruebas unitarias.
Obviamente, verificará las condiciones de borde y se asegurará de que el barajado tenga lugar exactamente como su algoritmo lo dicta dadas las entradas apropiadas.
Probar el generador seguro de números aleatorios
Si no está seguro de que el generador seguro de números aleatorios para su idioma no sea realmente aleatorio o tenga errores (proporciona valores fuera de rango, etc.), debe realizar un análisis estadístico detallado de la salida durante varios cientos de millones de iteraciones. Trace la frecuencia de ocurrencia de cada número y debería aparecer con la misma probabilidad. Si los resultados se sesgan de una forma u otra, debe informar sus hallazgos a los diseñadores del marco. Definitivamente estarán interesados en solucionar el problema, ya que los generadores seguros de números aleatorios son fundamentales para muchos algoritmos de cifrado.
fuente
Bueno, nunca estarás 100% seguro, así que lo mejor que puedes hacer es que es probable que los números sean aleatorios. Elija una probabilidad: digamos que aparecerá una muestra de números o elementos x veces dado un millón de muestras, dentro de un margen de error. Ejecute la cosa un millón de veces y vea si está dentro del margen. Afortunadamente, las computadoras hacen que este tipo de cosas sea fácil de hacer.
fuente
Para probar que una fuente de números aleatorios está generando algo que al menos tiene la apariencia de aleatoriedad, quisiera que la prueba generara una secuencia bastante grande de bytes, que los escribiera en un archivo temporal y que luego fuera a la herramienta ent de Fourmilab . Déle al conmutador -t (conciso) para que genere un CSV fácil de analizar. Luego, verifique los distintos números para ver si son "buenos".
Para decidir qué números son buenos, use una fuente conocida de aleatoriedad para calibrar su prueba. La prueba casi siempre debe pasar cuando se le da un buen conjunto de números aleatorios. Debido a que incluso una secuencia verdaderamente aleatoria tiene una probabilidad de generar una secuencia que parece no ser aleatoria, no puede obtener una prueba que seguramente pasará. Simplemente selecciona umbrales que hacen que sea improbable que una secuencia aleatoria cause una falla en la prueba. ¿No es divertido el azar?
Nota: No puede escribir una prueba que muestre que un PRNG genera una secuencia "aleatoria". Solo puede escribir una prueba que, si pasa, indica cierta probabilidad de que la secuencia generada por el PRNG sea "aleatoria". ¡Bienvenido a la alegría de la aleatoriedad!
fuente
Caso 1: Prueba de una barajadura:
Considere una matriz [0, 1, 2, 3, 4, 5], barajela, ¿qué puede salir mal? Lo habitual: a) sin barajar en absoluto, b) barajando 1-5 pero no 0, barajando 0-4 pero no 5, barajando y generando siempre el mismo patrón, ...
Una prueba para atraparlos a todos:
Mezcle 100 veces, agregue los valores en cada ranura. La suma de cada ranura debe ser similar entre sí. Avg / Stddev se puede calcular. (5 + 0) /2=2.5, 100 * 2.5 = 25. El valor esperado es de alrededor de 25, por ejemplo.
Si los valores están fuera de rango, existe una pequeña posibilidad de que obtenga un falso negativo. Puedes calcular qué tan grande es esa posibilidad. Repite la prueba. Bueno, por supuesto, hay una pequeña posibilidad de que la prueba falle 2 veces seguidas. Pero no tiene una rutina que elimine automáticamente su fuente, si la prueba unitaria falla, ¿verdad? Ejecútelo de nuevo!
¿Puede fallar 3 veces seguidas? Tal vez deberías probar suerte en la lotería.
Caso 2: tira un dado
La pregunta del lanzamiento de dados es la misma pregunta. Lanza los dados 6000 veces.
fuente