Java >> Programma Java >  >> Java

Test unitario di un metodo shuffle su un elenco

Considera la seguente classe:

public class Deck {
    private final Queue<Card> queue = new LinkedList<>();

    public Deck() { }

    public Deck(final Collection<Card> cards) {
        Objects.requireNonNull(cards);
        queue.addAll(cards);
    }

    public void add(final Card card) {
        Objects.requireNonNull(card);
        queue.add(card);
    }

    public void addAll(final Collection<Card> cards) {
        Objects.requireNonNull(cards);
        queue.addAll(cards);
    }

    public void shuffle() {
        Collections.shuffle((List<Card>)queue);
    }

    public Card take() {
        return queue.remove();
    }
}

Come testerei l'unità shuffle() metodo? Sto usando JUnit 4 per il test.

Ho le seguenti opzioni:

  1. Prova shuffle() per vedere che non genera un'eccezione.
  2. Prova shuffle() e controlla se il mazzo viene effettivamente mischiato.

Esempio di pseudocodice dell'opzione 2:

while notShuffled
    create new Deck
    take cards and check if they are shuffled

L'unico colpevole qui è che quando si esegue un test scritto per l'opzione 2 (che include ereditariamente anche l'opzione 1), se la riproduzione casuale non funziona come previsto, l'esecuzione del codice non si interromperà mai.

Come potrei risolvere questo problema? È possibile limitare il tempo di esecuzione nei test JUnit?

Risposta

Attualmente, la tua classe è strettamente collegata al Collections.shuffle funzione. Le funzioni statiche sono famose per rendere le cose più difficili da testare. (Inoltre, non ha senso testare Collections.shuffle; presumibilmente, funziona correttamente.)

Per risolvere questo problema, puoi introdurre una cucitura nella tua classe per questa funzionalità di mescolamento. Questo viene fatto estraendo il shuffle funzione in un ruolo (rappresentato da un'interfaccia). Ad esempio:

public interface ICardShuffler {
    void shuffle(List<Card> cards);
}

Quindi, il tuo Deck class può essere configurata per mantenere un riferimento a un'istanza di qualche implementazione di questa interfaccia e invocarla quando necessario:

public class Deck {
    private final Queue<Card> queue = new LinkedList<>();

    private ICardShuffler cardShuffler;

    public Deck(ICardShuffler cardShuffler) {
        this.cardShuffler = cardShuffler;
    }
    ...
    public void shuffle() {
        cardShuffler.shuffle((List<Card>)queue);
    }
    ...

Ciò consente al tuo unit test di utilizzare un test double, come un oggetto fittizio, per verificare che si verifichi il comportamento previsto (cioè che shuffle invoca shuffle sul ICardShuffler fornito ).

Infine, puoi spostare la funzionalità corrente in un'implementazione di questa interfaccia:

public class CollectionsCardShuffler implements ICardShuffler {
    public void shuffle(List<Card> cards) {
        Collections.shuffle(cards);
    }
}

Nota:oltre a facilitare il test, questa cucitura ti consente anche di implementare nuovi metodi di mescolamento senza dover modificare il codice in Deck .


Etichetta Java