Tool Choice
-----------
The first thing you'll notice when you start investigating unit testing is that there are about a million different unit test frameworks out there. I've used a number of them and finally settled on one. That doesn't mean that you'll settle on the same one. We all look for different things in a frame work. What I found is that most are good at the basics. The key is finding one that fits into your work flow best.
The framework I'm currently using is called UnitTestPlusPlus(replace the word plus with the symbol. For some reason it doesn't want to let me type the symbol today). I caught a presentation on real world unit testing at GDC last year and they mentioned this piece of tech so I checked it out. Soon after using it I fell in love with it. Here's why:
1. It works. This isn't a huge problem with other frameworks but I haven't had a single problem catching a crash or exception using this tech. It was also easy to get working. No scrambling for help on the web. It was just plug and play.
2. It's simple. A lot of other frameworks make you jump through a bunch of hoops to implement a simple test. You might need to have a list of tests in a separate header file or have your tests all marked up with macros everywhere. None of this is needed with UnitTest++. All you need to do is have the macro TEST(testname) at the start of the test and you're done. I can't tell you how nice it is to not have to deal with extra crap. It might not seems like that big of a deal but after you do it a few hundred times you'll be sick of such crap.
3. It's easy to integrate right into the VS build process. A lot of frameworks build your tests into a separate dll that you need to launch from a separate test app which gives you results. This seems all fun and cool because now you have a sexy little GUI with green lights for passes and red lights for fails, etc. What it really means is that any time you have a test fail you then have to attach to that process to debug it. Pain in the ass. With UnitTest++ it just runs as a post build event which means you can pop right into the debugger right there. Once again this doesn't sound like a big deal but after a few times of attaching to a process in Visual Studio and your IDE hangs you'll see the beauty of it.
4. Speed and size. UnitTest++ is fast and small even the output is just a small line of text in your build output window. Minimal = good for me.
A couple quick examples
-----------------------
Here's a couple quick tests as seen in my environment:
The first is simply creating an AIManager object to ensure it can be created. I'm not testing for anything else but the fact that it doesn't crash. Let's hope my coding isn't that bad :)
The second is creating a simple scene graph using mock objects and having my AIManager try to work the scene. My mock gameobjects just set a bool value if they've been worked or not.
I keep all of my mock objects in their own namespace to keep confusion to a minimum. In my last refactoring pass I went back and created base interfaces for all of my engine objects so creating mock objects would be super simple. I wasn't this wise my first time through which made it a little harder but not impossible. One of the best things about implementing unit tests is that it forces you to not only think of how to implement something but how to make it easily testable too.
//// Create and destroy the object//TEST (CreateAndDestroy){ Quicky::Common::CAIManager *Temp; Temp = new Quicky::Common::CAIManager(); delete Temp;}//// Work a scene with one branch containing 3 levels//TEST (ValidOneBranch){ Quicky::Common::CAIManager *Temp; Quicky::Mocks::CMockScene *MockScene; Quicky::Mocks::CMockGameObject *MockObject1; Quicky::Mocks::CMockGameObject *MockObject2; Quicky::Mocks::CMockGameObject *MockObject3; MockScene = new Quicky::Mocks::CMockScene(); MockObject1 = new Quicky::Mocks::CMockGameObject(); MockObject2 = new Quicky::Mocks::CMockGameObject(); MockObject3 = new Quicky::Mocks::CMockGameObject(); Temp = new Quicky::Common::CAIManager(); MockObject1->AddChild(MockObject2); MockObject2->AddChild(MockObject3); MockObject1->m_AI = true; MockObject2->m_AI = true; MockObject3->m_AI = true; MockScene->m_TopObject = MockObject1; Temp->SetCurrentScene(MockScene); Temp->WorkScene(1000); CHECK (MockObject1->m_bWorked == true); CHECK (MockObject2->m_bWorked == true); CHECK (MockObject3->m_bWorked == true); delete Temp; delete MockObject1; delete MockObject2; delete MockObject3; delete MockScene;}
Sigh :(