I have been learning quite a few development strategies as of late, and the newest one that I’ve taken a peek into is Test Driven Development (TDD), or Unit Testing. The idea behind TDD and Unit Testing is to split up your problems into small, easily manageable problems rather than having to design everything from the get go, and living with any or all of your mistakes for the rest of the project. TDD is a very complex topic and usually requires the use of various frameworks for dynamic unit tests, but I’ll be focusing on the ‘beginning’ method of TDD, static unit tests.
How do we start? Well in my current engine, what I did was write a class called “TestGame.cpp” which was a singleton class that housed various methods for testing the game. In that class, I would write functions to test a particular class I would be creating, or an entire game loop to test the logic of a class without having to construct a game world for our object to live in. Inside the TestGame
class there is a function called ’startTesting()
’ which will call anything you wish to test. Lets write a sample test to give you an idea on the very basics of what I’m doing.
void TestGame startTesting()
{
testActorClass();
}
void testActorClass()
{
Actor *act = new Actor();
string testString;
float testFloat;
act->setAlpha( .50f );
testFloat = act->getAlpha();
act->setIdentity( "newIdentity" );
testString = act->getIdentity();
act->setName( "Werewolf ");
testString = act->getName();
act->setPosition( Vector2::ZERO() );
testString = act->getPosition();
act->setSize( Vector2(32, 32) );
testString = act->getSize();
act->update( .003f );
}
Now if I were to compile this code, it would spit out a bunch of errors. “Set Name? .. Get Identity? I don’t know how to do that!” and that’s the point! These functions have not been written yet. The basic idea is to construct what you want your actor class to do, and then write the code to DO it. The next step is to write JUST enough code to get the project to compile, so… to the actor class we go! Quite literally, all we’ll be doing in the next step is to write the skeleton functions like such (NOTE: The various helper classes like Vector2
have been written and added to the engine, they are already created and have already been tested using this approach and do not come ’standard’ in C++ (or Obj-C as far as I know);
public:
bool setIdentity( const string &value );
string getIdentity() const { return identity; }
bool setName( const string &value );
string getName() const { return name; }
bool setPosition( const Vector2 &value );
Vector2* getPosition() { return position; }
bool setSize( const Vector2 &value );
Vector2* getSize() const { return size; }
bool setAlpha( const float value );
float getAlpha() const { return alpha; }
virtual void update( const float deltaTime );
protected:
string identity;
string name;
Vector2 *position;
Vector2 *size;
float alpha;
bool Actor::setIdentity( const string &value )
{
}
bool Actor::setName( const string &value )
{
}
bool Actor::setPosition( const Vector2 &value )
{
}
bool Actor::setSize( const Vector2 &value )
{
}
bool Actor::setAlpha( const float value )
{
}
void Actor::update( const float deltaTime )
{
}
This code should now compile and run, even though it doesn’t do anything. Well we know what we want to do, and we now have a skeleton to do it… so we should get going on writing the code!
For example, we know that we want the identity of the actor to be unique, and that the setIdentity function should only set the string if it currently does not have an identity so since this is going to be the most difficult function to write, let's do that one real quick.
bool Actor::setIdentity( const string &value )
{
if ( identity.length() != 0)
{
return false;
}
identity = value;
return true;
}
Really, that's it. The actor doesn’t know if its identity is unique, nor does it know how to obtain a unique identity. It just says “Okay, I don’t have an identity right now, so I’ll take this one and make it my own!” The idea is the actor class exists on its own, and functions as its own data. To obtain a unique identity and to keep track of all actors in the game world, we will use an ActorController
(remember that pesky thing called the Model / View / Controller? ) The Actor
class is the data (model) while the Controller will manage that data and when needed, present it to the view for rendering.. but that is a topic for another post.
The last thing we should do is COMMENT COMMENT COMMENT! I am a big fan of commenting my code, and documenting not only what the function does but why its implemented the way it is. If you ever look over your code 3 months down the line, you’ll say to yourself “I didn’t write this… what was I thinking?!” … well if you commented your code, you’d know! its a good habit to get into, and if you look at my Screen Controller from a few posts back, you’ll notice just how much I comment.
So that is all for today. Test Driven Development is a HUGE area, and this is just the very very basics of how it can be done. Once you get into the frameworks, it can be pretty complex, but very much worth the effort.
Happy coding everyone!
Read original blog post here