Showing posts with label tdd. Show all posts
Showing posts with label tdd. Show all posts

Tuesday, 27 May 2008

Top-down vs. bottom-up design

I've been having a think about top-down (a.k.a. outside-in) design during my recent iterative development exercise. In the series I've been putting off testing from the client layer down, primarily because GUIs have a reputation for being hard to test and harder to test-drive, and I wanted to make some early, easy progress on the core logic of the game.

I started to think that this approach might be a mistake. I'm dealing with the model that I think we'll need, not one demanded from the primary client of the model -- the GUI. Chad and Ben mentioned in their recent screencast that bottom-up implementation tended to lead to mistaken assumptions about infrastructure required by the top layers. I saw a similar point made on the BDD mailing list by Pat Maddox. Pat wrote (emphasis mine):

"I find an outside-in style of development to be very helpful... It forces you to think of your objects at a high level, so your design is driven by real need, and then you apply your design skills as you go on. When I use a pure bottom-up style, I write more speculative code and go down the wrong path far more often than I'd like. That's not to say that it's a problem inherent with that style, but rather a problem that I've personally experienced, and have more or less solved by using an outside-in approach."

This is opinion is echoed in an unrelated post to the TDD mailing list by Olof Bjarnason:

"I've been using TDD [bottom-up] for 2 years now, and it's been mostly a _great_ experience. The thing that bothers me most with "classic TDD" is that sometimes I build too much functionality in my classes, which isn't used in the end application after all. Even whole objects are wasted in the worst case."

The view here is that bottom-up design can lead to speculation and waste. By having a design driven directly by the overall, required behaviour, you only implement (and test) things that directly serve that behaviour. This can help eliminate speculative implementations of lower-level behaviour based on what you think the overall required behaviour will be.

Not so fast...

Sounds great! So what about inside-out / bottom-up / middle-out design? Ron Jeffries recently stated on the TDD mailing list that he generally prefers to start with the model (unless the project is simply to build a viewer). Maybe there's a bit more to it?

Digging further into that thread on the TDD list, there are a number of great points of view on the topic. Some TDD-ists argue that bottom-up design lets you build in small, easy steps, and refactor your way to the required behaviour that you would otherwise start with in top-down design. Others state that this leads to waste -- writing code and tests that just get refactored away. Which resulted in a couple of great quotes on the difference between refactored code and waste:

"I suppose we could also call the scaffold we use when constructing a large building as waste, or the safety harnesses as waste." -- John Roth to TDD list
"The analogy with scaffolding for a house is an excellent one - there is a lot of "stuff" constructed when building a house, *just* to support the construction - it is then discarded." -- Casey Charlton to TDD list

Top-down design can also lead to a "mockist" approach to TDD, where you need to mock all the required dependencies to implement the high level behaviour. This isn't necessarily a bad thing, but over-reliance on mocking can result in fragile tests. Martin Fowler has a great article on the pros and cons of "classic" and "mockist" TDD.

Enough rambling already!

While planning for part 3 of my recent development exercise I was coming to the conclusion that top-down was the way to go. After looking into it some more I was reminded of a whole host of advantages of bottom-up design. Even more importantly it reminded me that there is no silver bullet, and there are times when either, or a mix of both, approaches are fine. All this started to sound familiar, so firing up Google I noticed that I had read something to this effect in Jeremy Miller's excellent (as usual) post on the topic (search for "Bottom Up versus Top Down", although the whole post is worth reading).

I think the most important conclusion I've reached during this ramble is that if you are working in iterations to deliver a complete slice of the application (top and bottom) then you're never going to go too far wrong. Any "waste" from a bottom-up approach will be minimal as you'll be working toward and implementing the top almost immediately. And you'll still end up with higher-level behaviour specified with unit tests. Likewise starting top-down you'll still get the advantages of designing in small steps, particularly as you drive down into the design.

Sunday, 18 May 2008

Garden Race Pt 2: Adding multiple players

This post is part of a series on the (very) basics of iterative development using the example of a simple Snakes and Ladders-like game. Links to each post in the series will be added to the index page.

Um, ok, so the customers (firstborn and I) weren't overly impressed with the demo from part 1. It did help illustrate the most pressing deficiencies though:

  • Doesn't support multiple players
  • Doesn't have anything even remotely resembling snakes or ladders, let alone fairies
  • No gui

Our story list currently looks like this:

  1. A player can roll the die, and then move that many spaces along the board.
  2. A player that ends his or her turn on a "feature square" (a square containing a creature or obstacle), will be moved to the square connected with that feature.
  3. There can be 1-4 players, and each player has their turn in sequence.
  4. A player that reaches the final square wins the game.

There are no GUI stories currently defined, but we'll need something resembling a GUI eventually. I am tempted to start working on some GUI stories, because I don't want to get too far through the code and then find that it won't play nice with a graphical interface. It is really the main point of this software after all. On the other hand, the Game class is still very basic, so maybe it would be a good idea to knock over one of the original stories on our list. Story 2 seems like it could tie in with the GUI pretty strongly -- the view will have to show the player's move, then perform some kind of animation in the event that the player lands on a feature square. How about Story 3? Pretty basic, and essential to the game. At least that way firstborn and I can race each other to the end of the command line demo :)

I'll just check with the customers... be right back.

Ok, customer is asleep, so I'll take that as "that's fine Dad" :)

Quick aside, normally we would estimate "points" or some other unit of how much effort each story would take, and how many units could be done in an iteration, then have the customer prioritise the stories for this iteration.

Our test list

What tests could we write for multiple players?

  1. Should be able to set the number of players for a new game
  2. For a new, 2 player game, both player's should be off-the-board (square 0)
  3. After first player's roll, current player should be player 2
  4. After first player's roll, current player's position should be off-the-board (square 0)
  5. First player rolls a 3, second player rolls a 2, then current player should be player 1 on square 3.

Frankly, I don't really like how these look. Implementation details keep coming to mind, and I want to ignore them and focus on what I want to achieve (not how I want to achieve it). Let's start with these anyway and we'll see how it goes.

Starting iteration 2

Running all our tests shows we are all green, and all good to go. The first test on the list looks easy -- set the number of players in the game. Let's do that one.

//From GameSpec.cs
[Fact]
public void Should_be_able_to_create_2_player_game() {
 var twoPlayerGame = new Game(10, 2);
 Assert.Equal(2, twoPlayerGame.NumberOfPlayers);
}
//From Game.cs
public int NumberOfPlayers { get { return numberOfPlayers; } }
public Game(int boardSize) {
 this.boardSize = boardSize;
}
public Game(int boardSize, int numberOfPlayers) {
 this.boardSize = boardSize;
 this.numberOfPlayers = numberOfPlayers;
}

What about the previous constructor that just takes the boardSize? Well that should probably just start a new one player game I guess. Let's write a test for how we think it should work.

[Fact]
public void New_game_should_have_1_player_by_default() {
 var onePlayerGame = new Game(10);
 Assert.Equal(1, onePlayerGame.NumberOfPlayers);
}

This fails because the number of players initialises to zero.

public Game(int boardSize) {
 this.boardSize = boardSize;
 this.numberOfPlayers = 1;
}

Fixed. Now let's look at test 2, checking the position of each player for a new game.

//From GameSpec.cs:
[Fact]
public void New_game_should_start_all_players_off_the_board() {
 var newThreePlayerGame = new Game(10, 3);
 var players = new[] {1, 2, 3};
 foreach (var player in players) {
  Assert.Equal(0, newThreePlayerGame.GetSquareFor(player));   
 }            
}

//From Game.cs:
public int GetSquareFor(int player) {
 return 0;
}

An obviously deficient implementation like this GetSquareFor() method suggests we need to writes some more tests to flesh out a better one.

[Fact]
public void Positions_should_be_correct_after_first_two_players_roll() {
 var threePlayerGame = new Game(10, 3);
 const int firstRoll = 3;
 const int secondRoll = 5;
 
 threePlayerGame.Roll(firstRoll);
 threePlayerGame.Roll(secondRoll);
 
 Assert.Equal(firstRoll, threePlayerGame.GetSquareFor(1));
 Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));
 Assert.Equal(0, threePlayerGame.GetSquareFor(3));
}

Now we are potentially looking at a bigger step. We need Roll() to affect only the position of the current player. We don't have the concept of a current player. We'll also probably need an array or similar structure to store each player's position. Roll() will then store update the position of the current player, and change the current player to the next player. The CurrentSquare implementation will probably need to change to refer to the current player too. And then we'll have to add code to change the position in the event the player lands on a feature square! Argh!

Stop worrying! Try baby steps...

Let's back up a bit. I'm fairly confident we can write up the code above, but it will only be covered by one test and we are touching a lot of the Game class without direct guidance from the tests. We still have this test on our test list: "After first player's roll, current player should be player 2". This deals with the concept of the current player without requiring addition position arrays. It should only affect the Roll() implementation. Let's skip our last test by updating the attribute to [Fact(Skip="Too big a step for now")] (I could delete the test and rewrite it if we need it, but it did illustrate our need to deal with the current player concept, so I'll leave it for now). I started off coding our new test with two separate assertions:

var newTwoPlayerGame = new Game(10, 2);
Assert.Equal(1, newTwoPlayerGame.CurrentPlayer);
newTwoPlayerGame.Roll(2);
Assert.Equal(2, newTwoPlayerGame.CurrentPlayer);

The split asserts are ugly, and we can split this into two more specific tests. Here's the passing code, which was written one step at a time (not shown is chaining the Game(int) constructor to Game(int, int), so everything gets initialised properly in either case. Check the download at the end for the finished code):

//From GameSpec.cs:
[Fact]
public void Current_player_for_new_game_should_be_player_1() {
 var newTwoPlayerGame = new Game(10, 2);
 Assert.Equal(1, newTwoPlayerGame.CurrentPlayer);
}
[Fact]
public void After_first_players_roll_it_should_be_the_second_players_turn() {
 var newTwoPlayerGame = new Game(10, 2);
 newTwoPlayerGame.Roll(2);
 Assert.Equal(2, newTwoPlayerGame.CurrentPlayer);
}
//From Game.cs:
public int CurrentPlayer { get; private set; }
public Game(int boardSize, int numberOfPlayers) {
 this.boardSize = boardSize;
 this.numberOfPlayers = numberOfPlayers;
 this.playerPositions = new int[numberOfPlayers];
 this.CurrentPlayer = 1;
}
public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 CurrentPlayer++;
}

Before we go back to the test we skipped, I'd like to flesh out more of the CurrentPlayer property. Fifth test on our list was "First player rolls a 3, second player rolls a 2, then current player should be player 1 on square 3". Let's do a simpler version and just verify that this scenario ends up with the correct CurrentPlayer.

//In GameSpec.cs:
[Fact]
public void After_all_players_have_had_a_turn_it_should_be_first_players_turn_again() {
 var newThreePlayerGame = new Game(10, 3);
 newThreePlayerGame.Roll(1);
 newThreePlayerGame.Roll(1);
 newThreePlayerGame.Roll(1);
 Assert.Equal(1, newThreePlayerGame.CurrentPlayer);
}
//In Game.cs:
public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 CurrentPlayer++;
 if (CurrentPlayer > NumberOfPlayers) CurrentPlayer = 1;
}

A quick refactor from the green bar

We now have a green bar (well, yellow if you count the skipped test I guess). Time to take a look for potential refactoring opportunities. I've been a bit slack about this up to now as I haven't noticed anything obvious while coding and haven't explicitly stopped to think about refactoring. I have a bad habit of doing this -- I do design work while writing the code to pass the test, rather than deferring it to the refactoring stage. This can lead me to generalising to early or changing the design without getting clear direction from the tests and passing implementation. Note to self: premature generalisation is one of the many roots of all evil :)

In this case we've done the Right ThingTM and written simple code to pass the test, then looked at refactoring based on what the current implementation needs, rather than what we think it will need. The code within Roll() contains the logic for selecting the next player as well as for updating the current square. Let's Extract Method to make this more obvious.

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 nextPlayer();
}
private void nextPlayer() {
 CurrentPlayer++;
 if (CurrentPlayer > NumberOfPlayers) CurrentPlayer = 1;
}

This is purely a matter of taste. I find it reflects the intention more. If you don't, then leave it un-refactored. :-) Either way, key lesson here (for me, you probably know it already :)) is to defer design stuff until the production code shows a clear need, or until a test is too hard to write. Either way, design from the green bar whenever possible.

Time to face the music...

We probably shouldn't put it off any longer. Let's re-enable the test we skipped earlier:

//In GameSpec.cs:
[Fact]
public void Positions_should_be_correct_after_first_two_players_roll() {
 var threePlayerGame = new Game(10, 3);
 const int firstRoll = 3;
 const int secondRoll = 5;
 
 threePlayerGame.Roll(firstRoll);
 threePlayerGame.Roll(secondRoll);
 
 Assert.Equal(firstRoll, threePlayerGame.GetSquareFor(1));
 Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));
 Assert.Equal(0, threePlayerGame.GetSquareFor(3));
}

This give the following assertion failure:

TestCase 'DaveSquared.GardenRace.Tests.GameSpec.Positions_should_be_correct_after_first_two_players_roll'
failed: Assert.Equal() Failure
Expected: 3
Actual:   0

This is failing because our implementation for GetSquareFor(...) stinks -- it's just returning 0. My fault, not yours. Let's get back to the green bar as soon as possible, then we'll worry about getting the design right. My original guess for passing this test was that we would need an array of player positions, and Roll() would just update the position for the current player. We have a current player concept in the code now, so let's chuck in an array and see how it goes:

//Bits and pieces from Game.cs:
public class Game {
    //...
 private readonly int[] playerPositions;
 //...
 public Game(int boardSize, int numberOfPlayers) {
  this.boardSize = boardSize;
  this.numberOfPlayers = numberOfPlayers;
  this.playerPositions = new int[numberOfPlayers];
  this.CurrentPlayer = 1;
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
  playerPositions[CurrentPlayer - 1] = CurrentSquare;
  nextPlayer();
 }
 //...
 public int GetSquareFor(int player) {
  return playerPositions[player - 1];
 }
}

Running this fails with a new message:

TestCase 'DaveSquared.GardenRace.Tests.GameSpec.Positions_should_be_correct_after_first_two_players_roll'
failed: Assert.Equal() Failure
Expected: 5
Actual:   8

It is failing on our second assertion, Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));. This gives us a good hint as to what's happening -- our first and second rolls of 3 and 5 are both being added to the same array index. Hold on, that's only half the story. Let's have a closer look at this:

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 playerPositions[CurrentPlayer - 1] = CurrentSquare;
 nextPlayer();
}

Brilliant Dave. What a fantastic coder I am :) I'm still adding all rolls to CurrentSquare, then assigning that to the current player position. In this case, CurrentSquare is 3+5=8 which fails our test. Why didn't you point this out? Luckily I had my tests to do it in your absence :) We'll fix it right now:

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 playerPositions[CurrentPlayer - 1] += dieValue;
 nextPlayer();
}

Tests pass. My own ineptitude aside, this test that was initially giving us troubles has been trivial to solve. We now have a green bar, so let's refactor. First obvious bit of duplication is the two square increments in the Roll() method. Let's update CurrentSquare to use our GetSquareFor(int player) method:

public int CurrentSquare { 
 get { return GetSquareFor(CurrentPlayer); } 
}
//...
public void Roll(int dieValue) {
 playerPositions[CurrentPlayer - 1] += dieValue;
 nextPlayer();
}

CurrentSquare is no longer a getter/setter, but is a convenient shorthand for GetSquareFor(int player). Our Roll() method is fairly clear. We could make it a little bit clearer by extracting a moveCurrentPlayer(int squares) method, but would also mean more indirection. I'll make you a deal, if we end up with a few playerPositions[CurrentPlayer - 1] style array indexes (the -1 is pretty ugly), then we do something about it. For now it is probably ok.

Looking at Game from the perspective of the S.O.L.I.D. principles, I think the main one we have to be wary of is violating the Single Responsibility Principle (SRP). Current Game is responsible for:

  • Number of squares on the board
  • Number of players
  • Keeping track of whose turn it is
  • Keeping track of each player's position
  • Knowing when the game has finished

Are these cohesive enough to count under the banner of one responsibility? I'm not sure, but I can't currently think of a better abstraction (feel free to leave comments :-)). For now let's stay conscious of this and we'll revisit it if it starts becoming a problem.

We're done? No we're not!

So are we done for our multiple players story? After running a larger test it appears so. However, after updating the demo we see some strange behaviour. An extract of from the demo code and the output are shown below:

//From main() in Program.cs:
const int numberOfSquares = 20;
const int numberOfPlayers = 2;
//...
Console.WriteLine("Creating a new game with " + numberOfSquares + " squares and " + numberOfPlayers + " players.");
var game = new Game(numberOfSquares, numberOfPlayers);
Console.WriteLine("Press any key to roll the die.");
while (!game.IsFinished) {
 Console.ReadKey(interceptKey);
 var dieRoll = GetDieValue();
 game.Roll(dieRoll);
 var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.CurrentSquare;
 Console.WriteLine("Player " + game.CurrentPlayer + " rolled a " + dieRoll + ". " + currentState);
}
//...
Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 2 rolled a 1. Now on square 0
Player 1 rolled a 3. Now on square 1
Player 2 rolled a 2. Now on square 3
...snip...
Player 2 rolled a 2. Now on square 18
Player 1 rolled a 2. Now on square 14
Player 2 rolled a 3. You won the game!

Why does player 2 have the first go? And why do they roll a 1 and yet are on square 0? The reason is highlighted in the code snippet above -- the Roll() method updates the Game so that it is the next player's turn. When the demo gets to writing out the current player's position and state, it is actually writing the details for the next player who is about to have their turn.

I believe this is a form of temporal coupling, or coupling in time. The CurrentSquare and CurrentPlayer methods depend on whether they are called before or after Roll. There is almost certainly going to be a problem with IsFinished as well, as that depends on the CurrentPlayer too.

I've been worried about this for a little while actually, especially when thinking about how the GUI will probably want to animate the initial move, then an additional move up or down the snake/ladder/critter type thing. It looks like we'll either have to trigger events from Roll, or break it up and have some kind of coordinator object.

I am quietly confident that we can change our design to support this when we get some tests around the "feature square" or GUI stories, and that these tests will drive us toward a decent design and implementation. Again, let's stay conscious of this coupling and work around it in our demo code. Remember that our demo code is not production code, so we are being a little less precious with it. Next iteration we'll make sure we write tests to cover this and drive a nicer design.

Here's the new main loop in the demo code:

while (!game.IsFinished) {
 var currentPlayer = game.CurrentPlayer;
 Console.ReadKey(interceptKey);
 var dieRoll = GetDieValue();
 game.Roll(dieRoll);
 var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.GetSquareFor(currentPlayer);
 Console.WriteLine("Player " + currentPlayer + " rolled a " + dieRoll + ". " + currentState);
}
Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 1 rolled a 5. Now on square 5
Player 2 rolled a 3. Now on square 3
(...snip...)
Player 2 rolled a 1. Now on square 10
Player 1 rolled a 5. Now on square 23
Player 2 rolled a 5. You won the game!

As predicted, our IsFinished property is having problems because of the coupling issue. We'll fix this, then we're done.

Test-first debugging

Let's write a test to expose the bug with the IsFinished implementation:

[Fact]
public void Game_should_finish_as_soon_as_any_player_reaches_the_end() {
 var threePlayerGame = new Game(5, 3);
 threePlayerGame.Roll(1);
 threePlayerGame.Roll(6);
 Assert.True(threePlayerGame.IsFinished);
}

This fails. At the time the assertion is evaluated player 2 has finished, but the current player is player 3, who is yet to finish. This should be straight-forward to pass from here:

public bool IsFinished {
 get {
   return playerPositions.Any(square => square >= boardSize);
 }
}

All the tests now pass. We've now removed the temporal coupling between Roll and IsFinished, and our demo now works as expected:

Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 1 rolled a 1. Now on square 1
Player 2 rolled a 1. Now on square 1
(...snip...)
Player 1 rolled a 2. Now on square 17
Player 2 rolled a 2. Now on square 15
Player 1 rolled a 3. You won the game!

Press a key to exit.

That's a wrap!

Not sure if this looks like a lot of work to you, but it actually represents only a couple of minutes of coding time, and a couple of minutes of thinking time (plus lots of time to write it all down in excruciating detail :-)). Point is that each iteration so far has been really quick despite (or because of?) involving lots of small steps.

You can browse or checkout the final code for this iteration from here. Thanks for making it this far! :)

Friday, 16 May 2008

Garden Race Pt 1: Snakes, ladders and iterations

This post is part of a series on the (very) basics of iterative development using the example of a simple Snakes and Ladders-like game. Links to each post in the series will be added to the index page.

For a while now I've wanted to write a Snakes and Ladders-style game for my daughter, both to be a nice nerdy Dad, and to practice some dev stuff. I was going to create "Warps and Wormholes", but firstborn was adamant she wanted fairies involved. And so I've ended up with "Garden Race", which is going to be a race for players to make it to one end of the garden aided by fairies, butterflies and other girlie stuff, while hindered by lizards, hoses, and other less-girlie stuff.

I intend to use iterative development for this project -- I'll start off with a thin slice of functionality and keep adding to it to build up a semi-usable game. This should accomplish two things: first, showcase my own ineptitude, and second, to help improve my TDD and development skills. While I'm not exactly sure how far I'll get with this project (free time is short at present), I can commit to being fairly honest during this process. If and when I stuff up, I'll write it down (I might excuse myself a quick spike here and there so the posts don't get bogged down to much, but the stuff ups will stay).

Note: MS has a free game design version of Visual Studio that might be useful if your aim is to make a good game for your kids (instead of just playing with dev stuff). There are also a few open source frameworks out there (like Childsplay).

For my starting point, I began with a rough solution structure (main Game project and a test project), chucked it in a local SVN repo, and setup some of the tools I'd need. I'm trying using a /src based configuration just for kicks, and might try nant just for something different. I am also trying XUnit.Net, instead of my usual NUnit test framework.

Requirements

Our customers, i.e. my firstborn (after some prompting) and I, want a Snakes and Ladders-type game involving fairies and other garden-dwelling folk. Here is the basic statement of what we want:

"Garden Race is a computer-based board game. Players take it in turns to roll a die, and then move the corresponding number of squares on the board. If the player lands on a square featuring a garden creature of obstacle at the end of their turn, then that creature or obstacle will move the player to a connected square on the board. The first player to reach the bottom of the garden (the end of the board) wins. We want fancy 3D graphics, surround sound, and it has to be ready yesterday."

Ever noticed how hard it is to succinctly describe a simple concept, even something as simple as this? Let's try and remove some ambiguity by boiling things down into user stories.

  1. A player can roll the die, and then move that many spaces along the board.
  2. A player that ends his or her turn on a "feature square" (a square containing a creature or obstacle), will be moved to the square connected with that feature.
  3. There can be 1-4 players, and each player has their turn in sequence.
  4. A player that reaches the final square wins the game.

That should probably be enough to get us going.

Planning and design for this iteration

In the spirit of iterative design we are going to first try to deliver a slice of functionality. As we are currently unsure of exactly how to present the customer with fancy 3D graphics and sound (at this stage the dev team (me) would like to try WPF or QT4, but we don't know all the requirements yet), let's focus on the core game functionality, which is pretty much captured by the user stories above.

For our first iteration we've agreed with the customer to deliver stories 1 and 4, which is basically a single player rolling a die and moving to the end of the board. Exciting game huh? But it should reveal some basics of how the game mechanics work, so it seems a safe place to start.

If we are picking nouns for potential classes in our game we might come up with Player, Die, Square, and Board, but we're definitely not going to use nouns as a basis for our design. A nice place to start might just be a Game or GameEngine class. What tests could we come up with for our first story?

  • A player should not be on the board until the roll the die and move.
  • A player that rolls a 4 should then be on the 4th square from the start.
  • A player that rolls a 2, and then a 6, should be on the 8th square from the start.
  • The game should finish once the player reaches the end.

We currently don't have enough information to implement all of this. How many squares are there on the board? What is the maximum value of the die used? What happens if the player is one square from the end, and rolls a 3? The first couple of points we will check with the customer, but it doesn't really affect our design. Let's test for the last case though, and we'll check with the customer if our assumption is correct:

  • A player that is one square from the end, and rolls a 3, should win the game.

Before we jump into this iteration, I'd be really interested to hear any up-front design ideas you have for this. If you were going to draw a UML class diagram or write up some CRC cards, what classes, data and relationships would you have? I've got my own ideas, but I don't want to influence my iterative design process too much at this point, nor do I want to deliberately come up with a shoddy upfront design and then marvel at how well my iterative design (hopefully) turns out. Please feel free to leave your design ideas in the comments or email me.

Right, let's start.

First tests

Let's look at the second test on our test list. It looks like an easy thing to do and should help us learn a little about the problem domain.

public class GameSpec {
    [Fact]
    public void Player_should_be_on_fourth_square_after_rolling_a_four() {
        var game = new Game();
        game.Roll(4);
        Assert.Equal(4, game.CurrentSquare);
    }
}

Not going to keep much of this, I'm pretty sure of that. But we need to start somewhere. By the way, if you aren't familiar with XUnit.Net, [Fact] == [Test]. Now let's pass the test:

public class Game {
    public int CurrentSquare;

    public void Roll(int dieValue) {
        CurrentSquare += dieValue;
    }
}

Now let's test two rolls, which is the third test on our list.

[Fact]
public void Player_should_be_on_eight_square_after_rolling_a_two_then_a_six() {
 var game = new Game();
 game.Roll(2);
 game.Roll(6);
 Assert.Equal(8, game.CurrentSquare);
}

This works without change, as we jumped straight to an obvious implementation for Roll(). We could have taken smaller steps and initially used CurrentSquare = dieValue; to pass the first test, the updated it to pass the second test, but we don't need to do that unless we aren't really sure how to proceed.

Let's add a third test to make sure the player starts off the board, or square 0.

var game = new Game();
Assert.Equal(0, game.CurrentSquare);

Again, this passes without modification. We also have a test on our list about finishing the game. We don't have too much information about how this works, so let's look at what we can test.

[Fact]
public void Game_should_finish_when_player_reaches_end_of_board() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(boardSize);
 Assert.Equal(10, game.CurrentSquare);
 Assert.True(game.IsFinished);
}

This introduces two new concepts: board size, and the game state as finished or not finished. To get this to compile we need to add non-default constructor to Game, which means we also need to explicitly add a default constructor if we want to stop our other tests from breaking. We also need to add an IsFinished property to Game. The following implementation compiles and passes all the tests.

public class Game {        
 public int CurrentSquare;
 public Game(int boardSize) {}
 public Game() {}
 public bool IsFinished {
  get { return true; }
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
 }
}

The IsFinished implementation obviously stinks, so let's also add a test around unfinished games.

[Fact]
public void New_game_should_be_unfinished() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 Assert.False(game.IsFinished);
}

I first did a trivial implementation:

public bool IsFinished {
 get { return CurrentSquare == 0; }
}

Which failed both my IsFinished tests. Oops, that should be CurrentSquare != 0. Tests now pass, but the implementation still stinks. Let's try this one:

[Fact]
public void In_progress_game_should_be_unfinished() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(5);
 Assert.False(game.IsFinished);
}

Which we can pass with this:

public class Game {
 private readonly int boardSize;  
 public Game(int boardSize) {
  this.boardSize = boardSize;
 }
 public bool IsFinished {
  get { return CurrentSquare >= boardSize; }
 }
 //...[snip]...

The last test on our list so far is to see what happens when we overrun the last square on the board.

[Fact]
public void Game_should_still_finish_when_player_overruns_last_square() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(boardSize + 2);
 Assert.True(game.IsFinished);
}

This passes because we used >= for the CurrentSquare/boardSize comparison. One thing I haven't been doing is the refactor step of the TDD red-green-refactor process. Let's look at Game:

public class Game {
 private readonly int boardSize;
 public int CurrentSquare;
 public Game(int boardSize) {
  this.boardSize = boardSize;
 }
 public Game() {}
 public bool IsFinished {
  get { return CurrentSquare >= boardSize; }
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
 }
}

Not much to refactor there, right? The tests have some duplication in the Game setup though. And the default constructor of Game looks fairly useless. Normally I would just whack that into a [SetUp] method, but XUnit.Net discourages this as it can make for non-obvious test contexts. My GameSpec tests currently want to execute in the one test context -- a new Game with 10 squares. It seems reasonable that my GameSpec HAS-A context, so let's add a game as a field with a descriptive name. I can then replace the game initialisation in each test with a simple reference to newTenSquareGame, and get rid of Game's default constructor.

public class GameSpec {
 private readonly Game newTenSquareGame = new Game(10);
 //...[snip]...
 [Fact]
 public void Game_should_finish_when_player_reaches_end_of_board() {
  newTenSquareGame.Roll(10);
  Assert.Equal(10, newTenSquareGame.CurrentSquare);
  Assert.True(newTenSquareGame.IsFinished);
 }
 //...[snip]...

If we start having lots of different contexts or complicated contexts we may want to revisit this, but the whole test fixture reads fairly well for now. Full disclosure: before this approach I mucked around with some crazy test structure ideas I have regarding tests and test contexts. I'm omitting that from this post as it was mainly for personal interest rather that something I would normally do.

So what's next? Our test list is empty, and we seem to have completed most of our first iteration, stories 1 and 4. If you remember, these basically covered moving a single player around the board, and being able to finish the game. This isn't really something we can show our customer though. You don't go showing a youngin' a bunch of unit tests when they are expecting a fairyised version of Snakes and Ladders. And we haven't even dealt with the concept of "rolling a die", we have just assumed a value. But we seem to have the basic functionality we promised for this iteration.

Customer demo

To finish this iteration, let's write a console app that will allow our customer to run through the current game logic. Here's what I'm thinking of:

  Creating new game with 20 squares.
  Press a key to roll the dice.
  You rolled a 4. Now on square 4.
  You rolled a 6. Now on square 10.
  ...
  You rolled a 2. You won the game!

At first I was thinking about creating a GameController class and writing some tests around that, then calling that from a simple console app. But we don't need that yet. So let's just do the simplest thing that will work for our demo. And because it is slapped together and not designed to specific requirements, let's just promise not to use any of this code in the actual product.

namespace DaveSquared.GardenRace.ConsoleFrontEnd {
    class Program {
        private static readonly Random random = new Random();
                
        static void Main(string[] args) {
            Console.WriteLine("Welcome to Garden Race! It's like Snakes and Ladders, only without the copyright violation!");
            Console.WriteLine();
            
            const int numberOfSquares = 20;
            const bool interceptKey = true;
            
            Console.WriteLine("Creating a new game with " + numberOfSquares + " squares.");
            var game = new Game(numberOfSquares);
            Console.WriteLine("Press any key to roll the die.");
            while (!game.IsFinished) {                
                Console.ReadKey(interceptKey);
                var dieRoll = GetDieValue();
                game.Roll(dieRoll);
                var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.CurrentSquare;
                Console.WriteLine("You rolled a " + dieRoll + ". " + currentState);
            }
            Console.WriteLine();
            Console.WriteLine("Press a key to exit.");
            Console.ReadKey(interceptKey);
        }

        static int GetDieValue() {
            return random.Next(1, 6);
        }
    }
}

A now for the moment of truth:

Console app showing our demo

Fantastic! Assert.That(this_game_rocks).SaidWith(sarcasm)! On the other hand, it took about 12 minutes to code this iteration up as well as write out tests (plus lots of time for me to type out this narrative), and our story list made it clear when we could stop. So let's go show our customers. I'm sure they'll be impressed...

To be continued...

Download

You can browse through or download this tremendously exiting code from davesquared.googlecode.com.

Thursday, 15 May 2008

Garden Race Series: Basics of iterative development and TDD

This page is an index page for the Garden Race series. This intention behind this series of posts is to teach myself a bit about the basics TDD and iterative development by working through an example of building a Snakes and Ladders-style game. Hopefully this example strikes a balance between being simple enough to write up as blog posts and being involved enough to for me to learn some things about iterative development that can translate to real work.

Note: This is post has been back-dated to appear before the other posts in the series. It was written after Part 2.

Friday, 18 April 2008

An assortment of basic TDD tips

I spent most of last week working strictly TDD-style on a work project. Now all the stuff I do at home is done using TDD, and I sometimes manage to test-drive a feature into a work project, but for a number of reasons I've never jumped 100% into test driving stuff for work.

For the first few days I had mixed results. The code and design was probably not significantly better or worse that I would have come up with without TDD. I did end up with a decent suite of tests for the code, which was a nice bonus, and the code took a similar amount of time to non-TDD for me.

The last couple of days were a very different story: I felt the code and design turned out significantly better and was written much faster than I would generally have done sans TDD. The whole process flowed wonderfully.

I thought I'd jot down a couple of rambling points I learned or re-learned during the week, for my own future reference and on the off chance I help someone else experimenting with TDD. These are all pretty basic points, but ones which I sometimes lose sight of while coding in anger :-).

When I'm not sure how to do something, my tests tend to drive the procedure instead of the design

This was something that occurred to me while test-driving some code to generate XML schemas (XSD). It had been a while since I had done anything manual with XSD, so while I knew roughly what what I was trying to do, I was not really sure of the details on how to do it. So I started writing tests about how I thought XSD worked, and responding to the feedback from those tests.

The end result was that my tests built up the procedure of how to generate XSD, rather than a good design for what I was trying to achieve.

While this can be helpful in itself to learn an API or something else that's a bit new to you, it is not what you really want for production code. So next time I'm in a similar situation I'll make sure I do a quick spike (possibly test-first if I think it will help) to learn how, before throwing it away and starting to test-drive the production code.

Avoid complex APIs until you really need them

Back to my previous XSD example, we have a few options as of .NET 3.5. First up, XSDs are just strings, so we could work at that level. They are also valid XML documents, so we could use XmlDocument, XDocument, or XmlWriter. Finally, .NET has a built in XmlSchema and related classes specifically for dealing with XSD documents. So which would you chose?

I started with strings, but quickly ran into the problem mentioned in my previous point: I had forgotten many of the details of XSDs that I needed to build up the right string. So I back tracked and jumped straight into XmlSchema, leaning on the API a bit to relearn everything. This introduced a lot of needless complexity into my design to enable it to play nicely with the API.

In retrospect I think I'd have been better off sticking with strings or another basic construct until I really needed a full featured API.

Have a clear Subject Under Test (SUT)

Especially when starting out with testing (any testing, not just TDD), I have occasionally managed to confuse myself setting up the test context -- the data and dependencies required for the test. I would sometimes end up testing the test data, rather than exercising the production code.

This is probably (hopefully?) a bit less stupid than it sounds, as sometimes your code will simply delegate to another object, in which case you can test the state (test data that comes back) or the iteraction itself (usings mocks).

It pays to be very concious of exactly what your Subject Under Test (SUT) is. I've found myself thinking increasingly in terms of the SUT, its dependencies, and its behaviour to be exercised in the test*. Once clear on these points, it's time to setup the dependencies to provide a specific testing context and test data, exercise the SUT, then verify that the SUT performs correctly in that context. Using a four-phase test structure can help here.

* I'm getting crazy ideas about single test contexts per class, where the test context becomes the class state. A bit like AMC but less to do with mocking and more to do with structure and easy reproduction of test context across related tests/specifications.

Use test difficulties to drive design

After completing a couple of tests on a class, I found there was a method that seemed like it should be protected, but that I still wanted to have tests around. There are lots of valid approaches to do this: using inheritance (either a test double, or self-shunt [PDF]), InternalsVisibleTo, generated mocks, reflection tricks, or just leaving the method public. But why should we have to go through these hoops just for testability's sake? Why is something so trivial requiring any complexity or workarounds?

This kind of signal is generally referred to as a "smell" -- something that just doesn't seem right, and it is this kind of feedback that TDD is very good at providing.

In this case the smell prompted me to have another look at the code, and I discovered that the method actually seemed to belong on another object. So I moved the method to the other class, and had the original SUT delegate to it. This meant I could test the now public method on its new class, while keeping proper encapsulation for the original class. And even more importantly, the design was now much cleaner and clearer (IMHO).

So if you come across something that seems a bit messier to test than necessary, it might be your test's way of telling you there is something amiss with your design. Sometimes you may look at it and decide it is worth a bit of mess, but other times it is a clear cue to refactor.

Use tests to drive through uncertainty

I was test driving a controller class that accepted requests for specific resources. I had tests for a VerifyAccess(Guid id) method which loaded some information about the resource with that ID, then ensured the caller had access. A few tests later and I had another method, GetResource(Guid id), which actually retrieved the resource. Now GetResource should really check the access as well.

//Pseudocode
public void VerifyAccess(Guid id) {
  //Get details about resource with id
  //Check access, throw if access denied
}
public Resource GetResource(Guid id) {
  //Get details about resource with id
  //Check access?
  //return resource
}

So I had the situation where both methods wanted to call each other. As the access verification logic was important, I also wanted to keep it testable in isolation from the code to retrieve the resource. It looked like a simple matter of extracting a private, helper method called from both methods, but I was not sure... there were a few ways I could do it, but the resulting method names and code structure all seemed a bit convoluted and unnatural.

My approach was to write a test that exposed the required behaviour. When doing something that calls GetResource, the attempt should fail if VerifyAccess would also fail. It was then just a matter of getting the test to pass.

My final implementation was much nicer than my initial, uncertain guess. Turns out that GetResource would be better suited as a protected method called GetResourceAndCheckAccess. Callers never actually had to get a resource, they only needed to use one. So the old tests around GetResource became tests around UseResource(Guid id) (I'm changing the scenario from the original problem as don't want to post work stuff verbatim, so the real names are a lot more natural. Hopefully the main idea is clear though). VerifyAccess also called GetResourceAndCheckAccess. I also ended up with a nice, private helper method, verifyAccessToResource(Resource resource), that took a loaded resource rather than an ID. The intention of the code was now obvious from the names and structure, and the tests acurately specified the required behaviour.

//Psuedocode
public void VerifyAccess(Guid id) {
  GetResourceAndCheckAccess();
}
protected Resource GetResourceAndCheckAccess() {
  //Get details about resource with id
  verifyAccessToResource(resource);
  return resource;  
}
private void verifyAccessToResource(Resource resource) {
  //Check access, throw if access denied.
}
public SomeOutput UseResource(Guid id) {
  Resource resource = GetResourceAndCheckAccess();
  return resource.DoSomething();
}

While it's obviously pretty simple to come up with a solution without going test-first, once the required behaviour was specified correctly and protected by automated tests, I found it much easier to work towards a usable implementation without having to worry about correctness all the time. Provided the tests stayed green, I knew the implementation satisfied the requirement. For me, this approach lets me focus on incrementally getting it right, without being distracted by the uncertainties caused by having many ways of proceeding to a "fina;" answer.

Hope this is of some help to someone. It was definitely helpful for me to explain it all in any case, so if you made it this far, thanks for listening! :-)

Monday, 25 February 2008

TDD is easy!

I've been following Jacob Proffitt's recent posts, which focus on challenging some of the conventional wisdom around TDD ([1], [2]. [3], cross-posted to TheRuntime.com). (At least, that is my guess at his intention. Alternatively he could just be trying to incite flamewars :)). I'm always happy to have my assumptions challenged, as such challenges can help lead to deeper understanding, so I've been careful not to dismiss Jacob's work and instead have had a good think about it all.

While I disagree with some of his assumptions, logic and conclusions, I definitely agree with the pointy-end of his argument: that plain old unit testing (POUT) without TDD is better than coding without any unit tests. This became especially apparent to me after reading Michael Feather's Working Effectively with Legacy Code, which helped me realise that once you had code under test, you can refactor and improve the design with not-so-reckless abandon, eventually bringing you to the nice, clean type of design that TDD aims to help you achieve in the first place.

Jacob suggests that the majority of benefits generally attributed to TDD are actually just benefits of POUT and good design principles, and because TDD is more difficult that POUT, the industry should focus on getting people unit testing independently of TDD. By focusing on this distinction between POUT and TDD, I started thinking about his assumption that TDD is more difficult that POUT. What is it about TDD that makes it so hard compared with POUT?

TDD itself is trivially simple: write a test, make it pass, refactor to improve the design. As far as processes go, that's dead simple. So why do people have trouble with it? Anecdotally, the kinds of problems I normally hear (and have experienced) are things like "How do I test code that relies on a database?", "How can I test this UI logic?", "How can I test this unit without setting up loads of dependencies?". There is a common thread to all these issues: "How do I test [X]?". Unit testing. POUT. Completely independent of TDD, people have problems designing testable software, and writing unit tests for their designs. Which is understandable: good design is hard.

So is TDD actually the easy part? Are POUT and general software design challenges the real sources of people's difficulties? Regardless of whether you use TDD or not, maybe the real challenge is unchanged: developing a good, testable design and writing effective unit tests for it.

If there is any truth to this then it does make a good case for TDD. The main aim of TDD is helping developers to get a good design that, as a side effect of the process, can be unit tested. Rather than increasing the barrier to entry for unit testing (or POUTing :)) as Jacob suggests, maybe TDD has the opposite effect by guiding developers to make better (or at least more informed) design choices. That's not to say that you can't get a good, unit tested design without TDD of course. It is simply to say that if you are having trouble with TDD, switching to POUT may not help you much. As you will still be facing the same design and testing challenges, but without the feedback provided by TDD, it might actually exacerbate the problem!

Wednesday, 6 February 2008

A brief look at the logic of TDD

[Test] 
public void Should_do_tdd() { 
  Assert.That(me.ShouldTdd, Is.EqualTo("Not sure?"));
}

Jacob Proffitt has a post questioning whether TDD provides any benefits over Plain Old Unit Testing (POUT). POUT itself has many benefits, many of which became apparent to me reading Michael Feather's Working Effectively with Legacy Code. In the comments on Jacob's post I said I thought it unlikely that TDD would be proved better than POUT, due to problems measuring, and even defining, software quality. Jacob replied that he wasn't interested in a proof, but was simply after the reasons why TDD might help. So I thought I would have a quick run through the logic of why TDD might help you write better software. I'm going to steer away from benefits of TDD that you can also get from POUT (like low coupling/high cohesion etc), and focus on the my interpretation of the rationale behind TDD.

Please note: I am not trying to convert anyone. I firmly believe that you should use what works for you. If a particular tool doesn't help you, don't use it. If you can't see any value from an approach and feel it is a waste of time to examine further, don't try it. I'm also far from an expert on TDD. But seeing as Jacob took the time to reply to my comment, I thought I should at least return the favour :-)

Quick TDD review

TDD is design tool/process. You write a test first that describes some behaviour that your production code should exhibit. You run the test and it fails because you haven't implemented that behaviour yet. You then write some minimal code that makes the test pass. Finally, you refactor the code to remove duplication and improve the design, re-running the tests to make sure you haven't broken the behaviour. This process leads to the TDD slogan: "Red, Green, Refactor", red and green being the colours of the status bar in most xUnit test frameworks to show failure and success.

So what?

What is the logic of that? Testing code that doesn't even exist yet? Crazy!

Well let's start with the obvious stuff. First up, you get unit tests. Unit tests are good, and let you refactor safely. POUT/test last obviously does this too. You get quick feedback on whether the code you have just written breaks anything. You can do this with a POUT approach as well, depending on how quickly you write your tests, or whether you use manual testing for feedback. TDD provides a bit more assurance that you will get the quickest possible feedback and will always have a decent amount of code coverage, but with sufficient discipline a POUT approach can accomplish all of this. And we trust our development team right? We don't need some process to force them to do the right thing.

TDD as a design tool

TDD is a tool for incrementally improving the design of your code. The unit testing side of it is simply a nice side effect, to the point where BDD has been proposed as an alternate presentation of the technique to eliminate the apparent confusion caused by the word "test". So how can TDD be used to improve design?

Well, first up you are specifying the exact behaviour you want to implement before writing the code to do it. This has several effects. You are writing the logical interface to your class before the class itself (interface, not public interface IInterface{}). This takes some of the guesswork out of determining how your code is going to be called. You have to deal with the interface to your class in order to test it, so if it is painful to use you can immediately tell and do something about it. According to my pseudo-logic, this can help ensure production code that needs to talk to your object should have a well designed interface through which to do so easily.

Specifying the behaviour you want first also helps focus you on one piece of code at a time. You are just trying to pass the test, not immediately trying to solve every problem the class will ever face. This can help reduce feature creep in your design. If you find that a "divide and conquer" approach can make problem-solving easier, then isolating the one thing you want to achieve in this way can also make coding easier.

If you have never written a class that exposes an interface that turns out to be fairly unusable in Real LifeTM, or if you have never worked for a while getting your class to handle a situation that will never actually occur (e.g. "What if this class needs to take a DateTime as an input instead of just ints?", followed by a large effort to produce a generic version of your class that is only ever called as MyClass<int>) then you are a much better developer than I (probably a given), and you may not get any benefit from TDD. TDD doesn't prevent these problems, but it can help make these problems immediately apparent and therefore potentially avoidable.

The refactoring step is an essential part of the design aspect of TDD. The idea is that you have just gotten your test to pass, so your class' behaviour for that specific test is correct. The very next step is to improve the design. Remove any duplication, extract any methods or rename variables as required to make the code more readable and understandable. Sprout any new classes that are required to better encapsulate the data or behaviour. Refactoring is not unique to TDD, but TDD helps you to perform these design improvements in small, hopefully easy, increments. If your test is difficult to write or implement, this feedback tells you your design might need tweaking, or that you initial specification needs more clarity. Your design is evolving based on continuous feedback from your immediate requirements and your actual code. This can help avoid large, difficult refactorings and unnecessary refactoring.

By improving the design at each step, in theory you make the next step easy to perform. The goal is having code that is always easy to change, making your more responsive to changes in business needs and requirements, and making bugs easier to fix (that's right! You still get bugs doing TDD! :-P).

Useless point on the "magic" of TDD

Aside from my convoluted version of the theory behind TDD, I feel TDD has the potential to change the way you think about coding. For me TDD had a profound effect on how I think about software design, to the point where even when not doing TDD I still find myself thinking in terms of passing tests and getting feedback from incremental coding steps. I feel this has made me a better developer, providing me with another way of thinking about problem. The extra perspective may not help you in every situation, or ever, but it does give you another avenue to pursue when you are stuck.

I also find I come up with much cleaner designs using this different perspective. The Robert Martin's Coffee Maker example [PDF] illustrates some OO design traps. In my experience the incremental design encouraged by TDD helps avoid some of those traps. Of course, this is all anecdotal hand-waving, hence this section's title :-)

Clarifications and conclusions

Refactoring, incrementally improving design, isolating behaviour, writing testable code, etc can all be achieved without TDD. TDD is just a tool to help you do all these things. If you do them fine without TDD, then TDD is probably not going to help you. If the technique sounds interesting to you, then your best bet is to give it a try and see if you see any benefits over and above POUT (I first got into TDD following this example. I converted it to C# and NUnit, commented out all but one test, and started coding).

I have not listed anything that is a dramatic, inspiring benefit of TDD. That is because TDD is simply a tool to help achieve what we are all trying to do anyway: writing good, well designed software. If you do that already then TDD may just seem like more work. This is probably why people that feel passionately about TDD can have a hard time selling it to people that are skeptical of it. It isn't dramatic. There is no "if you do this you will become almost as popular as Justice Gray" aspect to it.

I should also be clear that, for me at least, TDD did not come easy. I am definitely still learning, but my confidence and my results improve the more I use it. I feel it has been worthwhile. YMMV. If you have a mentor or a colleague to learn with this process may be easier.

As a closing aside, I think everyone will agree that at least people are debating the merits or TDD vs. a simple, good bank of units tests, rather than trying to justify the practice of unit testing itself. :-) Hopefully that means I won't ever have to face the "We don't have time to unit test" discussion again ;-)

Friday, 11 January 2008

Implementing Python-like Range in C#

During a practice coding session last night, I was writing some tests and ran into a small dilemma. I wanted to execute a method call in a loop. Wow, what a tough problem, right? :-) Anyways, my problem was that putting the method call in a for loop sort of obscured the intention of the test. Unrolling the loop was clearer, but was a pain to write.

/* Unrolled loop. Pretty obvious what calls we are expecting. */
using (mockery.Record()) {
  renderer.WriteCurrentPage(1);
  renderer.WritePageLink(2);
  renderer.WritePageLink(3);
  renderer.WritePageLink(4);
  renderer.WritePageLink(5);
}
...
/*Less typing, but I'm expecting, um, 4 calls, oh I see, rendering pages 2 to 5 */
using ( mockery.Record()) {
  renderer.WriteCurrentPage(1);
  for (int page=2; page<=5; page++) {
    renderer.WritePageLink(page);         
  }
}

Parsing the second sample is admittedly pretty straight forward, but I found myself liking the first style as the scenarios being tested became more complicated. What I wanted was something a bit like the Python range() function. It works like this:

>>> print range(1,10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(10, 1, -1)
[10, 9, 8, 7, 6, 5, 4, 3, 2]
>>> print sum(range(1,10))
45
>>> print (map(lambda i: i * 10, range(1,10)))
[10, 20, 30, 40, 50, 60, 70, 80, 90]

So I thought I'd code up something similar in C# for a bit of fun (depending, of course, on your definition of "fun"). Just to be clear, I wanted a different implementation to Python's. In Python the range stops before the second parameter, so range(1, 3) gives [1, 2]. In my case I want [1, 2, 3].

A few provisos. I like provisos, because when someone points out my stupidity I can always say "It's ok! I had provisos!". First, this has probably been done before. Second, this is probably a bad idea, breaking normal loop semantics and so on. Third, this is probably a bad idea for reasons I haven't thought of yet. But, in the name of fun (definition pending), I will proceed. You can download the code I ended up with if you want to have a play around with it.

.NET 3.5 version

This section rewritten on 14 February 2008 after reading Steve Harman's post on System.Linq.Enumerable. Jay Wren has a good post on this too.

Turns out that .NET 3.5 already has a range implementation that completely meets my basic requirements:

IEnumerable<int> oneToTen = Enumerable.Range(1, 10);

I did search the relevant documentation when I initially wrote this post, but didn't find this .NET 3.5 addition until Steve Harman's post. However, the implementation covered in the rest of this post does a bit more than the built in functionality: it lets you specify different step values, does "smart" default step sensing, uses a fairly fluent interface, and is also a reasonable TDD exercise.

This implementation can also be used as a base to generate non-integer ranges, like ranges of DateTime. Jon Skeet also wrote a Range implementation for his book (which I didn't know when I first wrote this post), that he extended in just this way.

Test driving the Range class

Let's write a few tests that show how we would like our implementation to work. (When I did this I did the tests and corresponding implementations one at a time, TDD-style. I also did them in a different order to that presented here. I'm combining stuff for the sake of brevity and coherency.)

[Test]
public void Should_be_able_to_get_enumerator() {
  List<int> rangeValues = new List<int>();
  foreach (int i in Range.From(1).To(2)) {
    rangeValues.Add(i);
  }
  Assert.That(rangeValues, Is.EqualTo(new int[] { 1, 2 }));
}

[Test]
public void Should_be_able_to_get_range_from_1_to_10() {
  Assert.That(Range.From(1).To(10).ToArray(), 
              Is.EqualTo(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }));
}

The implementation to pass them looked a bit like this:

public class Range : IEnumerable<int> {
  private readonly int start;
  private int stop;

  public Range(int start) {
    this.start = stop = start;
  }
  public static Range From(int startRange) {
    return new Range(startRange);
  }
  public Range To(int endRange) {
    stop = endRange;
    return this;
  }        
  public IEnumerator<int> GetEnumerator() {      
    int current = start;
    while (current <= stop) {
      yield return current;
      current++;
    }
  }
  IEnumerator IEnumerable.GetEnumerator() {
    return GetEnumerator();
  }
}

Right, before plunging into my goal of mapping method calls across this range, I started thinking about negative ranges. In Python you can use range(10, 1, -1) to get [10, 9, 8, .., 2]. Maybe we should have that? I don't want to have to specify a step of -1 though. It should figure that out for itself.

[Test]
public void Should_be_able_to_get_range_from_10_to_1() {
  Assert.That(Range.From(10).To(1).ToArray(), 
              Is.EqualTo(new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }));
}

And while we're at it, let's give users the option to specify a step if they really want to. They might want every second number in a range.

[Test]
public void Should_be_able_to_set_a_step_value() {
  Assert.That(Range.From(1).To(10).WithStep(2).ToArray(), 
              Is.EqualTo(new int[] { 1, 3, 5, 7, 9 }));
}

We don't want them getting OutOfMemoryExceptionS thrown while specifying steps though, so we'll need to make it tough to specify an infinite range.

[Test]
public void Should_handle_step_values_that_immediately_go_out_of_range() {
  Assert.That(Range.From(1).To(10).WithStep(-1).ToArray(), 
              Is.EqualTo(new int[]{1}));
  Assert.That(Range.From(10).To(1).WithStep(2).ToArray(), 
              Is.EqualTo(new int[] { 10 }));
  Assert.That(Range.From(-10).To(1).WithStep(-1).ToArray(), 
              Is.EqualTo(new int[] { -10 }));
}

So carefully, test at a time, we get an implementation for GetEnumerator<int>() that looks a bit like this:

public class Range : IEnumerable<int> {
  ...
  private int step;
  public Range WithStep(int step) {
    this.step = step;
    return this;
  }
  ...
  public IEnumerator<int> GetEnumerator() {
    int max = Math.Max(start, stop);
    int min = Math.Min(start, stop);
    if (step == default(int)) {
      step = (start == min) ? 1 : -1;
    }
    int current = start;
    while (current >= min && current  <= max) {
      yield return current;
      current += step;
    }
  }
  ...
} 

So far so good. Now let's make it easy to perform an operation over a Range. We'll add a Do() method on Range.

[Test]
public void Should_be_able_to_do_sum_with_range() {
  int sum = 0;
  Range.From(1).To(5).Do(i => sum += i);
  Assert.That(sum, Is.EqualTo(1 + 2 + 3 + 4 + 5));
}

And to pass that:

//Range.cs
public void Do(Action<int> f) {
  foreach (int i in this) { f(i); }
}

As an aside, it's also nice to know that our current implementation plays nicely with LINQ. The following test passes without modification:

[Test]
public void Should_be_able_to_use_linq_with_range() {
  String oneToThree = "";
  foreach (int num in Range.From(1).To(5).Where(i => i <= 3) ) {
    oneToThree = oneToThree + num + " ";
  }
  Assert.That(oneToThree, Is.EqualTo("1 2 3 "));
}

Original goal within range...

Bad pun, sorry. We can now set our sights on rewriting the original test from the start of this post using the Range class.

[Test]
public void Test_original_mocking_scenario() {
  MockRepository mockery = new MockRepository();
  IPagerRenderer renderer = mockery.CreateMock<IPagerRenderer>();
  using (mockery.Record()) {
    //This is what we expect Range.Do to call, 
    //so we obviously won't use the Range class here.
    renderer.WriteCurrentPage(1);
    renderer.WritePageLink(2);
    renderer.WritePageLink(3);
    renderer.WritePageLink(4);
    renderer.WritePageLink(5);
  }
  using (mockery.Playback()) {
    renderer.WriteCurrentPage(1);
    Range.From(2).To(5).Do(i => renderer.WritePageLink(i));
  }
}
public interface IPagerRenderer {
  void WriteCurrentPage(int i);
  void WritePageLink(int i);
}

The emphasised section of the code has the same effect as the code snippets at the start of this post. You can compare the mockery.Record() to the mockery.Playback() sections to see the difference. To me this reads nicely: for each number in the Range From 2 To 5 Do the renderer.WritePageLink operation. I'm not about to going using this everywhere or anything, but I'm considering using it when it helps to communicate the intention of a test. All in all, 'twas a bit of harmless fun.

Grabbing the code

You can get a CS file containing the tests and Range code used for this post from here [links direct to CS file download]. It requires .NET 3.5, NUnit 2.4+ and Rhino Mocks (I used v3.3) to get it to work without modification. Should go without saying, but don't assume it works or is suitable for real life -- it's for solely for the purpose of mucking around :-)

Tuesday, 30 October 2007

Fluently constructing test objects

I have recently been playing around with TDD'ing a simple Pager control (like the one rendered at the bottom of the Google search results when your search returns multiple pages). For each test I ended up with a lot of recurring setup code (the typically repeated code is emphasised below):

[Test]
public void TestPagerAbbreviatesHeadOfList() {
  Pager pager = new Pager(2);
  StubPagerRenderer renderer = new StubPagerRenderer();
  pager.Renderer = renderer;
  pager.MaximumPageLinksToDisplay = 1;
  pager.CurrentPage = 2;
  pager.Render();
  string[] expectedPages = { "...", "2" };
  Assert.That(renderer.Pages, Is.EqualTo(expectedPages));
}

Traditionally a SetUp method has used to perform common initialisation tasks between test runs, but this practice is beginning to be discouraged, and after some initial doubts I am inclined to agree. I have frequently found my SetUp methods end up setting initial states that are overridden in some tests and not others, which quickly devalues the common initialisation and results in confusing tests (to assert something you need to check the SetUp method and check if the test is changing any of the default state). In fact, while I find I commonly need to setup common objects, most tests will require their own data relevant to that test. So does that mean I have to live with the duplication of the first 5 lines shown in the test above?

Well, being extremely prejudiced against duplication, I decided to create a method to create a new pager as a field in my fixture, with the relevant data passed in as parameters:

[TestFixture]
public class PagerBehaviour {
  private StubPagerRenderer renderer;
  private Pager pager;
  [SetUp]
  public void SetUp() {
    renderer = null; 
    pager = null;
  }
  void createPagerAndRenderer(int? currentPage, int? numberOfPages, int? maxLinksToDisplay) {
    pager = new Pager();
    renderer = new StubPagerRenderer();
    pager.CurrentPage = currentPage ?? pager.CurrentPage;
    pager.NumberOfPages = numberOfPages ?? pager.NumberOfPages; 
    pager.MaximumLinksToDisplay = maxLinksToDisplay ?? pager.MaximumLinksToDisplay; 
  }
  ...

I still used a very basic SetUp method here, just to force every test to explicitly recreate the pager (if they don't they'll get a null pointer exception -- I don't want a test accidentally using the state from a previous test). Here is what a revised test looks like:

[Test]
public void TestPagerAbbreviatesHeadOfList() {
  createPagerAndRenderer(2, 2, 1);
  pager.Render();
  string[] expectedPages = { "...", "2" };
  Assert.That(renderer.Pages, Is.EqualTo(expectedPages));
}

This has solved one problem, the duplication of the setup code, but hasn't solved the second, which is giving the test an obvious view of the Pager's state. How on earth am I meant to be able to determine the current page from createPagerAndRenderer(2, 2, 1)? Obviously this solution stinks pretty badly.

What I really want is an obvious way of passing the data, while still retaining brevity and minimal duplication. My current approach is to add a bit of complexity to get this by using a small class to build up the pager.

[TestFixture]
public class PagerBehaviour {
  private StubPagerRenderer renderer;
  private Pager pager;

  /* ... same SetUp() as last example ... */

  PagerAndRendererBuilder createPagerAndRenderer() {
    pager = new Pager();
    renderer = new StubPagerRenderer();
    pager.Renderer = renderer;
    return new PageAndRendererBuilder(pager);
  }

  class PagerAndRendererBuilder {
    private readonly Pager pager;
    public PagerAndRendererBuilder(Pager pager) {
      this.pager = pager;
    }
    public PagerAndRendererBuilder WithTotalPages(int numberOfPages) {
      pager.NumberOfPages = numberOfPages;
      return this;
    }
    public PagerAndRendererBuilder CurrentlyAtPage(int currentPage) {
      pager.CurrentPage = currentPage;
      return this;
    }
    public PagerAndRendererBuilder ShowingMaximumOf(int maximumLinksToDisplay) {
      pager.MaximumPageLinksToDisplay = maximumLinksToDisplay;
      return this;
    }
  }
  ...

My initial test now looks like this:

[Test]
public void TestPagerAbbreviatesHeadOfList() {
  createPagerAndRenderer().WithTotalPages(2).ShowingMaximumOf(1).CurrentlyAtPage(2);
  pager.Render();
  string[] expectedPages = { "...", "2" };
  Assert.That(renderer.Pages, Is.EqualTo(expectedPages));
}

True, this may classify as a bit of a misuse of a fluent interface, but to me it eliminates the duplication of creating the renderer and pager whilst providing an easy, readable way of passing test-specific data. The test only needs to deal with the initialisation that is relevant to it. The only cost is a slight increase in complexity, but to me this is more than offset by the increased readability.

Friday, 21 September 2007

xUnit.net announced

Interesting times in the area of automated testing for .NET. James Newkirk, who previously worked on NUnit (and wrote at least one good book on TDD in .NET), and Brad Wilson have released xUnit.net, a new unit testing framework for .NET. Read the announcement for the important differences.

One of the interesting features added (old news for MbUnit users, perhaps) is "test method extensibility", which is (emphasis mine):

"...the definition of how to run a test method can be extended. There are two example of this: the first, in the extensions DLL, is the [Theory] attribute which allows data-driven tests; the second, in the samples, is the [RepeatTest] attribute which runs a test method multiple times in a row. For more information on data theories, see http://shareandenjoy.saff.net/2006/12/new-paper-practice-of-theories.html."

There is also a comparison between NUnit, MSTest and xUnit.net on the xUnit.net CodePlex site. It is released under the Microsoft Permissive License (Ms-PL) (both NUnit and MbUnit use zlib/libpng-based licences).

Be interesting to see the reaction to this and the effect (if any) on the other unit testing frameworks, NUnit in particular. NUnit is pretty much the defacto standard for .NET unit testing, despite generally being seen to lag behind alternatives like MbUnit. If this move is seen as diluting the NUnit standard it might see a large jump of people to xUnit.net, or perhaps to the established and more advanced MbUnit.

[Update 2007-09-22] Some initial, lukewarm reactions have gone up around the 'Net. See Roy and Bill's posts. Brad Wilson has responded to some of these.

Thursday, 13 September 2007

Writing beautiful tests (from Beautiful Code)

Alberto Savoia's contribution to the O'Reilly-published Beautiful Code is about writing beautiful tests. The chapter is available online and features a walk-through of testing a binary search implementation in Java.

This might be a good TDD exercise -- test drive the implementation and then compare notes/tests with Alberto's article.

Tuesday, 28 August 2007

Groups post on writing a CSV generator using TDD

I saw a great post today on the TDD Yahoo! group regarding test driving a CSV generator. J.B. walks through the thought process behind each test, as well as refactoring steps. It is a very comprehensive example that is worth coding up if you (like me) are still learning TDD.

UPDATE 2007-08-30: Corrected terminology -- the example is generating CSV, not parsing it.