- Adding a Game Class, Test-First
- Limiting the Class with Tests
- At Last, Here Comes the Code
Limiting the Class with Tests
Now we want to ensure that we can add multiple players, up to the maximum of 10 allowed in a typical Texas Hold ’Em game (see Listing 3).
Listing 3 Support for multiple players.
public void testAddMultiplePlayers() { for (int i = 0; i < Game.CAPACITY; i++) game.add("" + i); List<String> players = game.getPlayers(); assertEquals(Game.CAPACITY, players.size()); assertEquals("0", players.get(0)); ... }
We add the following line to Game in order to get testAddMultiplePlayers to compile:
public static final int CAPACITY = 10;
Do we really want to specify each and every number from one through the capacity in the test? J2SE 5.0 gives us a simpler way of constructing the test (see Listing 4).
Listing 4 Adding a test helper method.
public void testAddMultiplePlayers() { for (int i = 0; i < Game.CAPACITY; i++) game.add("" + i); assertPlayers("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); } private void assertPlayers(String... expected) { assertEquals(expected.length, game.players().size()); int i = 0; for (String player: game.players()) assertEquals(expected[i++], player); }
We can now improve the readability of our other two tests, as shown in Listing 5.
Listing 5 Refactored GameTest code.
public void testCreate() { assertPlayers(); } public void testAddSinglePlayer() { final String player = "Jeff"; game.add(player); assertPlayers(player); }
The new test, testAddMultiplePlayers, should automatically pass with no other changes to the Game class. Does that mean that we didn’t need the test? Well, first, it would have been possible to craft the code in a more incremental fashion, so that the test didn’t pass initially. Second, the addition of the test forced the introduction of a limit on the number of players. The test now documents that limit. Change the name of the test to reflect that specification:
public void testAddMaximumNumberOfPlayers() { ...
Do we need another test to ensure that no more than 10 players are added to a game? The answer helps to distinguish TDD from testing techniques, or from something like design by contract.
Right now, we’re in complete control of what happens with the Game class:
- Think: "We’re going to code our clients so that they only add up to 10 players."
- Don’t think: "We have to write code to protect us from ourselves—from writing client code that adds more than 10 players."
Why would we build a system that allows adding more than 10 players? When we get to the point of providing a user interface, we’ll design the interface so that the system can’t possibly allow adding an eleventh player. Of course, we’ll write tests to help build that constraint into the interface.
If this makes you feel uncomfortable, go ahead and write a test to show what happens when you add an eleventh player. Maybe it throws an exception. But I’m confident enough that I’m not going to build a system that poorly. I also have my tests to guide me—reading what the tests say, I know how many players I can add to a game. By omission, adding too many players is undefined, so I’d better not do that.
Take some time to reflect on the tests we’ve created. Grab a fellow coder. First show him or her the names of the tests. Do they describe the capabilities of the Game class well? Ask your coworker to paraphrase the tests out loud. Can your associate quickly read and comprehend the test methods?
It’s easy to crank out a lot of test code in a short time. Remember that the refactoring part of the TDD cycle doesn’t just apply to your production code. You must continually rework the tests to eliminate unnecessary duplication and to make them clearly express the specifications they represent. There’s a subtle balance here—it’s possible to over-refactor tests so that they don’t read clearly.