Crafting Java with Test-Driven Development, Part 6: Refactoring Tests
- Adding a Game Class, Test-First
- Limiting the Class with Tests
- At Last, Here Comes the Code
Adding a Game Class, Test-First
Our progress in building the poker game has been a bit sluggish. Learning something new, particularly something as dramatic as test-driven development (TDD), doesn’t come for free. But now that we’ve gotten past learning some of the basics, we should start to see some increased productivity. In this installment, we’ll try to start banging out some code.
We currently have a couple of core classes, Deck and Card. On their own, they’re not very useful, but they are key to building the Texas Hold ’Em application.
Let’s move on and build a Game class—test-first, of course (see Listing 1). We can play Texas Hold ’Em with up to 10 players. We’ll drive out some simple support for creating a game and for adding a single player to the game.
Listing 1 Starter tests for the Game class.
package domain; import java.util.*; import junit.framework.*; public class GameTest extends TestCase { private Game game; protected void setUp() { game = new Game(); } public void testCreate() { assertEquals(0, game.players().size()); } public void testAddSinglePlayer() { final String player = "Jeff"; game.add(player); List<String> players = game.players(); assertEquals(1, players.size()); assertEquals(player, players.get(0)); } }
The production code we end up with that meets these test specifications is pretty straightforward (see Listing 2).
Listing 2 Initial Game implementation.
package domain; import java.util.*; public class Game { private List<String> players = new ArrayList<String>(); public void add(String player) { players.add(player); } public List<String> players() { return players; } }
The derivation of this code isn’t nearly as straightforward. We don’t type these two classes all at once. Test-driving them into existence means a lot of back-and-forth between the test class and the production class. We code a little bit of test (maybe a single assert), run the JUnit test suite to demonstrate failure, code just enough to production test, run the JUnit test suite to demonstrate success, clean up the code, run the JUnit test suite to ensure that nothing got broken. We repeat that cycle a few times. It sounds like a lot of work, but it’s not. I spent less than three minutes to get the code above in place.
Following the rule of TDD—we don’t build any more than our tests currently specify—we moved through the following steps:
- testCreate simply specifies a need for a numberOfPlayers method; it initially hard-codes a return of 0.
- testAddSinglePlayer forces us to introduce a counter internal to Game. We initialize this counter to 0, return it from numberOfPlayers, and set it to 1 when someone adds a player.
- We complete testAddSinglePlayer by adding an assertion to prove that the player name was correctly stored. This drives out the need for constructing an ArrayList and storing the player name in it. We also eliminate the need for numberOfPlayers, since we can derive the size from the ArrayList.