2010-05-30 40 views
10

Estoy trabajando en un pequeño proyecto para mí en este momento y lo estoy usando como una oportunidad para familiarizarme con las pruebas unitarias y mantener la documentación adecuada.JUnit Método de prueba con naturaleza aleatoria

Tengo una clase Deck con una baraja de cartas (es muy simple y, para ser honesto, puedo estar seguro de que funciona sin una prueba unitaria, pero como dije, me estoy acostumbrando a usar pruebas unitarias) y tiene un método shuffle() que cambia el orden de las cartas en el mazo.

La aplicación es muy simple y sin duda trabajar:

public void shuffle() 
{ 
    Collections.shuffle(this.cards); 
} 

Pero, ¿cómo podría aplicar una prueba de unidad para este método. Mi primer pensamiento fue verificar si la carta superior del mazo era diferente después de llamar al shuffle(), pero por supuesto existe la posibilidad de que sea lo mismo. Mi segundo pensamiento fue verificar si todo el orden de las tarjetas ha cambiado, pero de nuevo podrían estar en el mismo orden. Entonces, ¿cómo podría escribir una prueba que garantice que este método funcione en todos los casos? Y, en general, ¿cómo pueden los métodos de prueba unitaria para los cuales el resultado depende de alguna aleatoriedad?

Saludos,

Pete

+1

Desde una perspectiva lógica, se consideraría sus cartas barajadas si el pedido es lo mismo? –

+1

Para su última pregunta, vea http://stackoverflow.com/questions/122741/testing-for-random-value-thoughts-on-this-approach. También creo que D. Knuth tiene un capítulo entero al respecto. – ewernli

+1

Creo que tiene sentido que el mazo se considere barajado si las cartas permanecen en el mismo orden y se han producido algunos reordenamientos, aunque es poco probable que sea posible barajar un mazo de cartas real en el mismo orden en el que estaban originalmente en. – Peter

Respuesta

5

La afirmación de si el grupo aleatorio método realmente baraja las cartas es muy difícil si no imposible. Los generadores de números aleatorios predeterminados son solo aleatorios en cierto grado. Es imposible probar si está satisfecho con este grado de aleatoriedad porque tomaría demasiado tiempo. Lo que realmente está probando es el generador de números aleatorios en sí mismo, que no tiene mucho sentido.

Lo que puede probar sin embargo, es el invariants de este método.

  • Si sale del método, debe haber el mismo número exacto de cartas en el mazo que cuando lo ingresó.
  • El método de mezcla no debe presentar duplicados.

Por supuesto, puede crear una prueba que compruebe que en una secuencia de n barajar no se devuelven las cubiertas duplicadas. Pero de vez en cuando, esta prueba puede fallar (aunque sea poco probable, como ya se indicó en otras respuestas).

Otra cosa a tener en cuenta es el generador de números aleatorios en sí. Si esto es solo un proyecto de juguete, es suficiente java.util.Random. Si tiene la intención de crear algún juego de cartas en línea, considere usar java.security.SecureRandom.

0

que suponga que tiene 52 cartas en la baraja. La posibilidad de obtener el mismo orden en dos llamadas posteriores es extremadamente baja, por lo que no me molestaría demasiado. Pero, si comienzas a obtener mazos similares varias veces, creo que es seguro decir que tienes algunos problemas con tu generador de números aleatorios.

Entonces, la respuesta: verifique que el orden sea diferente para todo el mazo.

Además, creo que puede hacer que sea un requisito para su método shuffle() no devolver las tarjetas en el mismo orden dos veces seguidas. Y si desea asegurarse de cumplir con ese requisito, puede verificar la similitud en la implementación del método.

+1

También verifique que el conjunto de cartas representadas por cada mazo sea el mismo (es decir, el mismo número de cartas, mismos miembros una vez que se ignora el orden). –

3

En primer lugar, vamos a pensar en las probabilidades involucradas:

  1. Usted no puede garantizar que el shuffle no colocar las tarjetas en el orden exacto. ¡Sin embargo, la probabilidad de hacer esto con un mazo de 52 cartas es 1/52! (Es decir, es mínima y probablemente no vale la pena preocuparse.)

  2. Definitivamente tendrá que comprobar toda la cubierta, aunque debido a la probabilidad de que la carta superior siendo el mismo que era antes de que el shuffle es 1/52.

En el caso general, y suponiendo que esté usando el generador de números java.util.Random, solo inícielo con la misma semilla. Entonces, la salida para una entrada predeterminada debe ser repetible.

Sin embargo, específicamente para este caso, suponiendo que no haya implementado su propio List Realmente no veo el punto en el ensayo Collections.shuffle(List<?> list) o Collections.shuffle(List<?> list, Random rnd) (API link) ya que estos son sólo una parte de la API de Java.

0

Interesante pregunta. En mi opinión, la mejor manera sería almacenar cada "mezcla" en una colección, luego compararla después de cada mezcla si tu mazo coincide con cualquiera de los "mazos" anteriores en la colección.

Dependiendo del ammount de "aleatoriedad" que requieren que incrementarán el importe de las cubiertas barajadas se almacenan en que esto es prueba de la unidad después de 50 barajaduras que tendría una colección de 50 "cubiertas"

2

Otro enfoque sería utilizar el método shuffle(List<?> list, Random random) e inyectar una instancia Random sembrada con una constante.

De esta forma, su prueba JUnit puede ejecutar una serie de llamadas y verificar que la salida sea la salida esperada.

La implementación normal de su clase crearía una instancia Random que no se sembró.

1

En realidad está delegando todo el trabajo duro en la clase java.util.Collections. Esta es una clase central en la API de recopilación de Java y debes asumir que funciona como probablemente lo hagas con la clase java.lang.String.

Prefiero recomendar el código contra las interfaces y simular/eliminar su clase de implementación con el método shuffle(). Luego puede afirmar que sus llamadas al método shuffle() se llaman realmente de su prueba en lugar de probar exactamente lo mismo que los tipos de Sun/Oracle han probado antes.

Esto le permite centrarse más en la prueba de su propio código, donde el 99,9% de todos los errores se encuentran probablemente. Y si, por ejemplo, reemplaza el método java.util.Collections.shuffle() con uno de otro marco o su propia implementación, ¡su prueba de integración seguirá funcionando!

Entiendo que estás haciendo esto porque quieres aprender y creo que el conocimiento sobre la lógica de tropezar/burlarse de otros marcos es muy útil como parte de tu conocimiento de prueba.

0

La mayoría de las personas parecen opinar que debe probar lo que está probando. Con eso me refiero a lo que estás creando (o integrando, cuando te estás asegurando de que una biblioteca de terceros realmente haga lo que dice).

Pero no se debe probar el lenguaje Java en sí.

Debe haber algún principio de prueba como "No probar PlusEquals".

0

He trabajado en números aleatorios en un marco de modelado y simulaciones y se presentó ante un problema similar: ¿Cómo puedo realmente unidad a prueba nuestras implementaciones PRNG. Al final, en realidad no lo hice. Lo que hice en cambio fue realizar algunos controles de cordura. Por ejemplo, nuestros PRNG anuncian cuántos bits generan, así que compruebo si esos bits realmente cambiaron (con 10k iteraciones más o menos) y todos los demás bits fueron 0. Comprobé el comportamiento correcto con respecto a las semillas (inicializando el PRNG con el mismo seed debe producir la misma secuencia de números), etc. Luego decidí poner las pruebas de aleatoriedad reales en una UI interactiva para que puedan ser probadas cuando lo deseen, pero para las pruebas unitarias un resultado no determinista no es tan agradable, pensé.

0

Se podría barajar varias veces, manteniendo un registro de cuántas veces el as de picas (o alguna otra tarjeta, o todas las otras tarjetas) termina como la primera carta de la baraja. En teoría, la tarjeta debería terminar en la parte superior de 1 de 52 mezclas. Una vez recopilados todos los datos, compare la frecuencia real con el número 1/52 y compruebe si la diferencia (valor absoluto) es inferior a algún valor épsilon seleccionado. Cuanto más baraje, más pequeño será su valor epsilon. Si su método shuffle() coloca la tarjeta en la parte superior dentro de su umbral épsilon, puede estar seguro de que está aleatorizando las cartas como a usted le gustaría.

Y usted no tiene que parar justo en la primera carta. Puede probar si cada ubicación en el mazo ofrece los mismos resultados. Hazlo con una carta, haz todas las cartas, probablemente no importe. Puede ser exagerado, pero garantizaría que su reproducción aleatoria() funciona correctamente.

Cuestiones relacionadas