Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing of a shuffle method on a list

Consider the following class:

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();
    }
}

How would I unit test the shuffle() method? I am using JUnit 4 for testing.

I have the following options:

  1. Test shuffle() to see that it does not generate an exception.
  2. Test shuffle() and check if the deck actually gets shuffled.

Example pseudocode of option 2:

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

The only culprit here is that when executing a test written for option 2 (which also inheritly includes option 1), if the shuffling does not work as intended, then the code execution will never stop.

How would I solve this issue? Is it possibly to limit the execution time in JUnit tests?

like image 438
skiwi Avatar asked Apr 16 '14 12:04

skiwi


People also ask

How do you test shuffle function?

1 Answer. Show activity on this post. What you can test is shuffle N times(use loop) in a test and it should be at least x% times different in total. For example, if I shuffle 100 times at least 70-80 times(or whatever acceptable percentage) %, it should be different.

Is shuffle method available in collection class?

The shuffle() is a Java Collections class method which works by randomly permuting the specified list elements. There is two different types of Java shuffle() method which can be differentiated depending on its parameter. These are: Java Collections shuffle(list) Method.

Can you shuffle a list in Java?

The java. util. Collections class provides shuffle() method which can be used to randomize objects stored in a List in Java. Since List is an ordered collection and maintains the order on which objects are inserted into it, you may need to randomize elements if you need them in a different order.

How do you shuffle elements in a collection?

Shuffle Array Elements using Collections Class We can create a list from the array and then use the Collections class shuffle() method to shuffle its elements. Then convert the list to the original array. Output: [1, 7, 5, 2, 3, 6, 4] Note that the Arrays. asList() works with an array of objects only.


3 Answers

Currently, your class is tightly coupled with the Collections.shuffle function. Static functions are notorious for making things more difficult to test. (On top of that, there's no point in you testing Collections.shuffle; presumably, it works correctly.)

In order to address this, you can introduce a seam in your class for this shuffling functionality. This is done by extracting the shuffle function into a role (represented by an interface). For example:

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

Then, your Deck class can be configured to keep a reference to an instance of some implementation of this interface, and invoke it when necessary:

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);
    }
    ...

This allows your unit test to use a test double, like a mock object, to verify that the expected behavior occurs (i.e., that shuffle invokes shuffle on the provided ICardShuffler).

Finally, you can move the current functionality into an implementation of this interface:

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

Note: In addition to facilitating testing, this seam also allows you to implement new methods of shuffling without having to modify any of the code in Deck.

like image 50
Lilshieste Avatar answered Oct 14 '22 11:10

Lilshieste


I don't understand your pseudocode... why use a while loop? Just call shuffle on a deck. If an exception is thrown, test fails. If the deck is in the same order, test fails. Do you need more than that?

like image 44
jgitter Avatar answered Oct 14 '22 09:10

jgitter


You could also write your own class CollectionsHelper and use that instead of Collections.

public class CollectionsHelper {
    private CollectionsHelper(){}
    private static boolean isTest = false;
    private static int n = 0;
    public static void shuffle(List<?> l){
        if(!isTest) Collections.shuffle(l);
        else Collections.shuffle(l, new Random(n));
    }
    public static void setTest(){
        isTest = true;
        n = 0;
    }
    public static boolean isTest(){
        return isTest;
    }
    public static void setSeedForTest(int seed){
        n = seed;
    }
}

At the beginning of each test you can call CollectionsHelper.setTest() to use the deterministic shuffle. Your class would look like:

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() {
        CollectionsHelper.shuffle((List<Card>)queue);
    }

    public Card take() {
        return queue.remove();
    }
}
like image 35
lolloz98 Avatar answered Oct 14 '22 11:10

lolloz98