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:
- Test
shuffle()
to see that it does not generate an exception. - 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?
Advertisement
Answer
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
.