My plan is as follows: I'm going to create a project with only the vaguest of notions as to where it may be going, and see just how useful TDD is going to be in helping me create maintainable code. As an added obstacle I have to say that I don't know much about TDD other than the principle of red-green-refactor.
So this exercise should be interesting.
Here's the concept of the software I'm going to create: A piece of software that creates a world that a player can walk through and do stuff. How's that for a fuzzy brief?
If I'm going to have a world that a player can navigate, it stands to reason that I need locations, and that my locations have to connect to other locations. And in order for a player to get to other locations, each location needs exits, which shouldn't point anywhere before they've been assigned. I'll also need to be able to get and set the location that an exit leads to, and each location should have a unique ID, which can be assigned to an exit in another location.
Here are the tests I've come up with:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestMethod] | |
public void Location_is_constructed_with_correct_Id() | |
{ | |
Location location = new Location(1); | |
Assert.AreEqual(1, location.Id); | |
} | |
[TestMethod] | |
public void Location_is_constructed_with_all_exits_set_to_no_exit() | |
{ | |
Location location = new Location(1); | |
Assert.AreEqual(-1, location.ExitInDirection(0)); | |
Assert.AreEqual(-1, location.ExitInDirection(1)); | |
Assert.AreEqual(-1, location.ExitInDirection(2)); | |
Assert.AreEqual(-1, location.ExitInDirection(3)); | |
} |
I implement the constructor and ExitInDirection method to return hard-coded values other than the expected test results in order to get my first reds, then flesh them out as follows, which gives me green tests:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static int _numberOfDirections = 4; | |
private int[] _exits; | |
public int Id { set; get; } | |
public Location(int id) | |
{ | |
// Store the id of the location | |
Id = id; | |
// Initialise the exits | |
_exits = new int[_numberOfDirections]; | |
for (int i = 0; i < _numberOfDirections; i++) | |
{ | |
_exits[i] = -1; | |
} | |
} | |
public int ExitInDirection(int direction) | |
{ | |
// Return the id of the location stored in the exits array in the specified direction | |
return _exits[direction]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestMethod] | |
public void Location_returns_no_exit_if_direction_is_negative() | |
{ | |
Location location = new Location(1); | |
Assert.AreEqual(-1, location.ExitInDirection(-1)); | |
} | |
[TestMethod] | |
public void Location_returns_no_exit_if_direction_is_greater_than_available_directions() | |
{ | |
Location location = new Location(1); | |
Assert.AreEqual(-1, location.ExitInDirection(4)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public int ExitInDirection(int direction) | |
{ | |
// If the direction exceeds the bounds of the exits array, return -1 | |
if (direction >= _numberOfDirections || direction < 0) | |
return -1; | |
// Return the id of the location stored in the exits array in the specified direction | |
return _exits[direction]; | |
} |
Another thing I need is a method to determine where each exit direction leads to. But so far I can't set the location for an exit, so that's my next goal.
First I'll create a method to link a location to an exit, which initially does nothing at all:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void SetExitToLocation(int direction, int destinationLocationId) | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestMethod] | |
public void CreateExitToLocation_stores_id_of_target_location_in_correct_direction() | |
{ | |
Location location = new Location(1); | |
Location destination1 = new Location(1); | |
Location destination2 = new Location(2); | |
Location destination3 = new Location(3); | |
location.SetExitToLocation(1, destination1.Id); | |
location.SetExitToLocation(2, destination2.Id); | |
location.SetExitToLocation(3, destination3.Id); | |
Assert.AreEqual(-1, location.ExitInDirection(0)); | |
Assert.AreEqual(1, location.ExitInDirection(1)); | |
Assert.AreEqual(2, location.ExitInDirection(2)); | |
Assert.AreEqual(3, location.ExitInDirection(3)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void SetExitToLocation(int direction, int destinationLocationId) | |
{ | |
_exits[direction] = destinationLocationId; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestMethod] | |
public void CreateOneWayExitToLocation_throws_exception_if_direction_is_negative() | |
{ | |
Location location = new Location(1); | |
Assert.ThrowsException<ArgumentOutOfRangeException>(() => location.SetExitToLocation(-1, 1)); | |
} | |
[TestMethod] | |
public void CreateOneWayExitToLocation_throws_exception_if_direction_is_greater_than_available_directions() | |
{ | |
Location location = new Location(1); | |
Assert.ThrowsException<ArgumentOutOfRangeException>(() => location.SetExitToLocation(10, 1)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void SetExitToLocation(int direction, int destinationLocationId) | |
{ | |
// If the direction exceeds the bounds of the exits array, throw an exception | |
if (direction >= _numberOfDirections || direction < 0) | |
throw new ArgumentOutOfRangeException("direction", "Value exceeds the limit of the array"); | |
else | |
_exits[direction] = destinationLocationId; | |
} |
P.S. Since I'm new to TDD, any constructive comments as to what could be improved in my process are very welcome.