As a general rule, I like to have a test for the common ways
things work as well as the exceptional cases, and I like to create tests which
reflect how the classes are used. We want to have at least a happy case and a
sad case. The happy case is where everything runs as expected, and obviously
the sad case is when it is the reverse. This can mean that we have an exception
thrown or that we handle the bad occurrence. Since they are easy to create, I
will start by handling an exceptional case. We need to now ask ourselves what
should happen if we run out of cards. After coming up with the answer, we
should create a test which states exactly that.
Listing 3: Handling Special Cases Testing
[Test]
public void EmptyDeckShouldReturnNullWhenDrawing()
{
// Create an empty Deck
var deck = new Deck(new List<Card>());
Card card = deck.DrawCard();
Assert.AreEqual(null, card, "A Card was returned from an empty Deck.");
}
The important thing we did here is that we defined what we
expect to happen here. This obviously doesn't happen yet, we get an exception
for now. We want to make sure that we get this expected result. Once we have
this test in place and passing, we know what to expect anywhere that calls this
method. When we execute this method, we get the exception we know we would get,
which is good. The System.ArgumentOutOfRangeException lets us know that the
application worked as we knew it would, because we have not written the code to
make this test pass yet. While we are in the test class, we will add more tests So this article doesn't get too out of hand, we will be creating multiple tests at a time. We will also add a test which makes sure that the Card we draw is no longer in the Deck.
Listing 4: More Testing
[Test]
public void DeckShouldNotContainACardAfterDrawingIt()
{
var deck = new Deck(new List<Card> { new Card(), new Card(), new Card() });
Card card = deck.DrawCard();
Assert.IsNotNull(card);
Assert.IsFalse(deck.ContainsCard(card));
}
This test will also fail, because we haven't created a
method to check if a card is in the Deck. If we add in an empty method that
just throws an Exception, we will have it fail for that reason. Now we need to
add in the code to make these two methods pass.
Listing 5: Implementing Passing Code
public bool ContainsCard(Card card)
{
return _cards.Contains(card);
}
Since we already wrote most of the code, this was all that
was needed to make our tests pass. Normally I write as few tests as possible at a time, so I can better focus on making sure each test is implemented correctly. This helps to make sure I am not diluting my focus when creating the interactions between objects.