Click here to Skip to main content
15,867,308 members
Articles / TDD

An Exploration of TDD - Part 1

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
7 Dec 2015CPOL3 min read 7.7K   9   4
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.

I've been squinting at TDD as a development concept for a while now. I'm coming from a background where all you did was testing after the fact (if you were lucky and had a patient boss), and I remember the pain of extending or changing code in such an environment. So TDD looked like a great concept to me, and now I've decided to put it to the test.

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:

C#
[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'm not entirely sure about the second test because, strictly speaking, it tests two things at the same time (the constructor and the ExitInDirection method), but I don't want my method of storing exits to be public, so until I figure out how to access private variables in a unit test, this'll have to do.

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:

C#
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];
}

I don't think I want to keep the number of possible exits to be hard-coded in the Location class, but that's for later. For now, I can't see any other tests that I could apply to the constructor, but the ExitInDirection method is another thing. I need to test that against a direction that's outside of the bounds of my array of exits.

C#
[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));
}

With the current code, both those tests are red as they throw an out of bounds exception, so now I refactor to account for invalid directions:

C#
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];
}

And with that amendment, all my tests are now green. Looking good so far.

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:

C#
public void SetExitToLocation(int direction, int destinationLocationId)
{
}

And some tests to check whether an exit links to the correct location after it's been created:

C#
[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));
}

Running this against the blank method gives me my initial red, so now I'm good to flesh out my SetExitToLocation method:

C#
public void SetExitToLocation(int direction, int destinationLocationId)
{
    _exits[direction] = destinationLocationId;
}

At this point, I have to ask myself again what should happen if the direction is outside of the bounds of the array. Right now, I get an index out of bounds exception, which isn't all that bad, but I figure that an argument out of range exception would make more sense. So I add two more tests for that:

C#
[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 gives me another red test until I amend my SetExitToLocation method to:

C#
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;
}

And that has my whole range of tests green again, and completes my first draft for my location class.

P.S. Since I'm new to TDD, any constructive comments as to what could be improved in my process are very welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO Creative Cats
United Kingdom United Kingdom
Frauke's love affair with computers started by drooling over the sheer awesomeness of a ZX81 when she was a child. Since then she has owned a Commodore 128, an Amiga, an Acorn RISC PC (would you believe it) and an assortment of more mainstream laptops and PCs.

Her love of computers and technology in general led her to obtain two Bachelor's degrees in engineering. She has worked both as a hardware and a software engineer, and has been the managing director of her own IT consultancy, Creative Cats, for 15 years now. She helps small businesses and entrepreneurs with their IT needs, providing hands-on support where needed and training where required.

She loves being asked why she decided to become an engineer.

To be able to go out wearing a red shirt and survive, of course.

Comments and Discussions

 
SuggestionGreat article! Have a suggestion... Pin
Steve Pinckney28-Dec-15 19:51
Steve Pinckney28-Dec-15 19:51 
GeneralRe: Great article! Have a suggestion... Pin
Frauke29-Dec-15 0:13
Frauke29-Dec-15 0:13 
Questionarticle Pin
Donsw8-Dec-15 9:37
Donsw8-Dec-15 9:37 
Nice job. it illustrates tdd very well. you do bring up the issue of the test should be for one thing. this way when it breaks you know why and where,
cheers,
Donsw
My Recent Article : CDC - Change Data Capture

GeneralMy vote of 5 Pin
Camilo Reyes7-Dec-15 16:14
professionalCamilo Reyes7-Dec-15 16:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.