2010-05-30 17 views
10

Attualmente sto lavorando a un piccolo progetto per me stesso e lo sto utilizzando come un'opportunità per familiarizzare con i test delle unità e la corretta documentazione.JUnit Metodo di prova con carattere casuale

Ho una classe Deck con rappresenta un mazzo di carte (è molto semplice e, per essere onesti, posso essere sicuro che funzioni senza un test di unità, ma come ho detto mi sto abituando a usare i test unitari) e ha un metodo shuffle() che cambia l'ordine delle carte nel mazzo.

L'applicazione è molto semplice e certamente lavorare:

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

Ma, come ho potuto implementare un test di unità per questo metodo. Il mio primo pensiero è stato quello di verificare se la prima carta del mazzo fosse diversa dopo aver chiamato shuffle() ma c'è ovviamente la possibilità che sarebbe lo stesso. Il mio secondo pensiero è stato quello di verificare se l'intero ordine delle carte è cambiato, ma ancora una volta potrebbero essere nello stesso ordine. Quindi, come potrei scrivere un test che garantisce che questo metodo funzioni in tutti i casi? E, in generale, come si possono testare i metodi per i quali l'esito dipende da una casualità?

Cheers,

Pete

+1

Da un punto di vista logico, le tue carte saranno considerate mescolate se l'ordine è rimasto lo stesso? –

+1

Per la tua ultima domanda, consulta http://stackoverflow.com/questions/122741/testing-for-random-value-thoughts-on-this-approach. Penso anche che D. Knuth abbia un intero capitolo a riguardo. – ewernli

+1

Penso che abbia senso considerare il mazzo mischiato se le carte rimangono nello stesso ordine e si è verificato un nuovo riordino, anche se è improbabile che sia possibile rimescolare un vero mazzo di carte nello stesso ordine in cui erano originariamente in. – Peter

risposta

5

Affermando se il riordino il metodo effettivamente mischia le carte è molto difficile se non impossibile. I generatori di numeri casuali predefiniti sono solo casuali fino a un certo grado. È impossibile verificare se sei soddisfatto di questo grado di casualità perché richiederebbe troppo tempo. Quello che stai effettivamente testando è il generatore di numeri casuali stesso che non ha molto senso.

Ciò che è possibile testare, tuttavia, è lo invariants di questo metodo.

  • Se si esce dal metodo, ci dovrebbe essere lo stesso numero di carte nel mazzo come quando lo si è inserito.
  • Il metodo shuffle non dovrebbe introdurre duplicati.

Si può naturalmente creare un test che verifica che, in una sequenza di n mescola non ci sono ponti duplicati restituiti. Ma una volta ogni tanto questo test può fallire (comunque improbabile, come già affermato in altre risposte).

Qualcos'altro da tenere in considerazione è il generatore di numeri casuali stesso. Se questo è solo un progetto giocattolo, è sufficiente java.util.Random. Se hai intenzione di creare un gioco di carte online, considera l'utilizzo di java.security.SecureRandom.

0

mi si supponga di avere 52 carte nel mazzo. La possibilità di ottenere lo stesso ordine in due chiamate successive è estremamente bassa, quindi non mi preoccuperei troppo di ciò. Ma, se inizi a ricevere mazzi simili più volte, penso che sia sicuro dire che hai qualche problema con il tuo generatore di numeri casuali.

Quindi, la risposta: controlla che l'ordine sia diverso per l'intero mazzo.

Inoltre, penso che puoi tranquillamente renderlo un requisito per il tuo metodo shuffle() non restituire le carte nello stesso ordine due volte di seguito. E se si vuole assolutamente assicurarsi di seguire tale requisito, è possibile verificare la similarità nell'implementazione del metodo.

+1

Controlla anche che il set di carte rappresentato da ciascun mazzo sia lo stesso (cioè lo stesso numero di carte, gli stessi membri una volta che l'ordine è ignorato). –

3

In primo luogo, pensiamo le probabilità coinvolte:

  1. Non si può garantire che il riordino non mettere le carte in ordine esatto. Tuttavia, la probabilità di farlo con un mazzo di 52 carte è 1/52! (ovvero è minimo e probabilmente non vale la pena di preoccuparsi.)

  2. Devi sicuramente controllare l'intero mazzo, anche se la probabilità che la prima carta sia la stessa di prima dello shuffle è 1/52.

Per il caso generale e presupponendo che si stia utilizzando il generatore di numeri java.util.Random, è sufficiente inizializzarlo con lo stesso seme. Quindi l'output per un input predeterminato dovrebbe essere ripetibile.

Tuttavia, appositamente per questo caso, supponendo che non hanno attuato il proprio List io non vedo proprio il punto in fase di test Collections.shuffle(List<?> list) o Collections.shuffle(List<?> list, Random rnd) (API link) in quanto questi sono solo una parte delle API Java.

0

Interessante domanda. Secondo me il modo migliore sarebbe quello di memorizzare ogni "shuffle" in una raccolta, quindi confrontare dopo ogni shuffle se il tuo mazzo corrisponde a uno dei precedenti "mazzi" nella raccolta.

A seconda della quantità degli "casualità" si richiede si aumenterà la quantità degli mazzi mescolate si memorizzano in quella cioè unit test dopo 50 riordini si avrebbe una collezione di 50 "ponti"

2

Un altro approccio sarebbe utilizzare il metodo shuffle(List<?> list, Random random) e iniettare un'istanza Random con una costante.

In questo modo il test JUnit può eseguire una serie di chiamate e controllare che l'uscita sia l'uscita prevista.

L'implementazione normale della classe creerebbe un'istanza Random non associata.

1

In realtà stai delegando via tutto il duro lavoro alla classe java.util.Collections. Questa è una classe centrale dell'API di raccolta di Java e dovresti semplicemente supporre che funzioni come probabilmente con la classe java.lang.String.

Preferisco raccomandare il codice contro le interfacce e simulare/stubare la classe di implementazione con il metodo shuffle(). Quindi puoi semplicemente affermare che le tue chiamate sul metodo shuffle() sono effettivamente chiamate dal tuo test anziché testare esattamente le stesse che i ragazzi Sun/Oracle hanno testato in precedenza.

Ciò consente di concentrarsi maggiormente sulla verifica del proprio codice in cui il 99,9% di tutti i bug si trovano probabilmente. E se per esempio sostituisci il metodo java.util.Collections.shuffle() con uno da un altro framework o la tua implementazione, il tuo test di integrazione funzionerà ancora!

Capisco che lo stai facendo perché vuoi imparare e credo che la conoscenza sullo stub/derisione della logica da altri framework sia molto utile come parte della tua conoscenza dei test.

0

La maggior parte delle persone sembra essere dell'opinione che si dovrebbe testare ciò che si sta testando. Con ciò intendo quello che stai costruendo (o integrando, quando stai facendo in modo che una libreria di terze parti faccia effettivamente quello che dice di fare).

Ma non si dovrebbe testare il linguaggio Java stesso.

Ci dovrebbe essere qualche principio di prova come "Non testare PlusEquals".

0

Ho lavorato su numeri casuali in un framework di modellazione e simulazione e mi sono trovato di fronte a un problema simile: come posso testare in modo unitario le nostre implementazioni PRNG. Alla fine, in realtà, non l'ho fatto. Quello che ho fatto invece è stato eseguire alcuni controlli di sanità mentale. Ad esempio, i nostri PRNG pubblicizzano tutti i bit che generano, quindi ho controllato se quei bit effettivamente cambiassero (con iterazioni di 10k o giù di lì) e tutti gli altri bit fossero 0. Ho controllato il comportamento corretto riguardo ai semi (inizializzando il PRNG con lo stesso i semi devono produrre la stessa sequenza di numeri), ecc. Quindi ho deciso di mettere i test di casualità reali in un'interfaccia utente interattiva in modo che possano essere testati quando lo si desidera, ma per i test unitari un risultato non deterministico non è bello, ho pensato.

0

Si può mescolare ripetutamente, tenendo traccia di quante volte l'asso di picche (o qualche altra carta o tutte le altre carte) finisce come prima carta nel mazzo. Teoricamente, la carta dovrebbe finire in cima a circa 1 su 52 shuffle. Dopo aver raccolto tutti i dati, confrontare la frequenza effettiva con il numero 1/52 e verificare se la differenza (valore assoluto) è inferiore a un valore epsilon scelto. Più si rimescola, più piccolo può essere il valore di epsilon. Se il tuo metodo shuffle() pone la carta in cima alla tua soglia epsilon, puoi essere certo che sta randomizzando le carte come vorresti.

E non devi fermarti solo sulla prima carta. Puoi verificare se ogni posizione nel mazzo dà gli stessi risultati. Fallo con una carta, farà tutte le carte, probabilmente non importa. Potrebbe essere eccessivo, ma garantirebbe che il tuo shuffle() funzioni correttamente.

Problemi correlati