How to write a test (of e.g. the .size() if an ArrayList that's been put into a HashSet that has a nested for each loop in it?

83 views Asked by At

I'm new to Java, on an apprenticeship, and struggling really. I'm wanting to write a junit5 test for this: (I should add, this project is not finished, I'm working my way through trying to create a game of Blackjack while learning along the way)

public Deck() {

    ArrayList<Card> cards = new ArrayList<>();
        //fill deck with cards
    for (Suit suit: Suit.values()) {
        for (Rank rank: Rank.values()){
            cards.add(new Card(rank,suit));
        };
    }
    HashSet<Card> deck = new HashSet<>(cards);;

now I can quite easily get the answer with: System.out.println(deck.size()); but that's cheating.

I can't quite figure out how to write a test for e.g. void thereAre52CardsInADeck(){ } without just putting the whole block of code in there. That seems inefficient.

Can anyone help/offer advice?

EDIT: I really am entirely new to this, and fairly new to coding full stop, so apologies. This is part of a learning exercise for me.

1 - by "System.out.println(deck.size())" cheating, I meant just having that at the end of my Deck() class (i.e. and not in a junit test class) is cheating because I'm wanting to unit test everything

2 - my suit class is:

public enum Suit {HEARTS, CLUBS, Diamonds, Spades}

my Rank class is

public enum Rank {
    ACE(1), DEUCE(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9), TEN(10), JACK(10), QUEEN(10), KING(10);
    private final int rankValue;

    private Rank(int rankValue) {
        this.rankValue = rankValue;
    }

    public int getRankValue(){
    return rankValue;
    }
}

My card class is:

public class Card {
    private final Rank rank;
    private final Suit suit;
    public Card (Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }
    public Rank getRank() {
        return rank;
    }

    public Suit getSuit(){
        return suit;
    }

    @Override
    public String toString(){
        return rank + " of " + suit;
    }

}

My main right now is: public class Main {

public static void main(String[] args){
    Deck deck = new Deck();
}

}

I've ran the project and know I can get the correct amount running it with System.out.println(deck.size()), and if I remove the .size() I get my full list too, but I've not managed to work it out in a Junit test.

3

There are 3 answers

2
cyberbrain On

Your code shows a constructor that uses local variables. You cannot unit test those inside details of a method or constructor.

You should unit test expected outputs of methods or constructors or interactions with other objects methods. (The interaction testing is a bit "higher level" but only until you find out how to use Mockito or another library that handles most stuff on that for you.)

If you keep that code in the constructor as it is, you cannot unit test anything there, as it has no side effects - except if you have unexpected side effects in the Suit.values() and Rank.values() (both enums I guess?) or in the constructor of Card.

Even the heap should be the same before and after as all objects probably get garbage collected after calling that constructor.

1
AudioBubble On

Tell me if this what you want? Or else I can modify if its not exactly what you asked for

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class DeckTest {

    @Test
    void thereAre52CardsInADeck() {
        Deck deck = new Deck();

        int deckSize = deck.getDeckSize();

        Assertions.assertEquals(52, deckSize, "The deck should contain 52 cards");
    }
}
0
Bentaye On

First, you don't need both cards and deck in your Deck class, just go with

public class Deck {

    private final Set<Card> cards;

    public Deck() {
        cards = new HashSet<>();
        for (Suit suit : Suit.values()) {
            for (Rank rank : Rank.values()) {
                cards.add(new Card(rank, suit));
            }
        }
    }

    public Set<Card> getCards() {
        return cards;
    }

}

Then what you need to do is create a test class, first declare the dependency on junit5, here is for maven:

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-engine</artifactId>
  <version>5.9.2</version>
  <scope>test</scope>
</dependency>

(See here if you are using another dependency management tool)

Then write the test this way:

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.*;

class DeckTest {

    private Deck deck;

    @BeforeEach
    void setUp() {
        deck = new Deck();
    }

    @Test
    void deckHad52Cards() {
        assertEquals(52, deck.getCards().size());
    }

}

Additionally:

As your cards are stored in a Set, that will ensure that there are no duplicate cards, but that only work if you override the equals method in your Card class, so add the following bit of code in Card

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    Card card = (Card) o;
    return rank == card.rank && suit == card.suit;
}

@Override
public int hashCode() {
    return Objects.hash(rank, suit);
}

and you can then test hat by creating a test class for Card, ie CardTest and have the 2 following tests in it:

@Test
void sameCardsShouldBeEqual() {
    assertEquals(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS));
}

@Test
void differentCardsShouldNotBeEqual() {
    assertEquals(new Card(Rank.KING, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS));
}

After all this, you have tested that you have 52 distinct cards in your deck.