XNA Framework GameEngine Development. (Part 3, TDD)
Introduction
Welcome to Part 3 of the XNA Framework GameEngine Development series. In this article, I will introduce the Test Driven Development piece of the game engine. Better late than never.
There are plenty of articles covering TDD so I will not argue the benefits here. I personally see TDD as just another tool for writing good code, starting early and writing test cases often is always “The Best Practice”. The simple fact that I am introducing TDD in the 3rd article of the series indicates I am not an extremist. I would be happy to entertain your thoughts and suggestions.
Creating a Unit Test Project (Updated to use Managers Namespace)
I chose to use NUnit as my unit tester, mainly because it is free and is what I use at work. Also, please note, I do not use the Express Additions of Visual Studio, I have no experience setting up TDD in VS Express. If you have questions or experiences please feel free to leave a comment. Updated, KaBaL was kind enough to provide some code for the Visual Studio Team Suite test cases, please see below.
First, create a new windows class library project, I named mine RoeEngine2UnitTests. You will need to reference your game engine project as well as, Microsoft.Xna.Framework, Microsoft.Xna.Framework.Game, and the nuit.framework. Most likely you will also have a couple System references at this point, keep those as well.
Next, Rename or Delete/Add the Class1.cs file to RoeEngineTestCases.cs and add the following code.
Note: Updated, thanks to KaBaL for pointing out that I renamed the namespace Manager to Managers.
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using RoeEngine2.Managers;
using Microsoft.Xna.Framework;
namespace RoeEngine2UnitTests
{
[TestFixture]
public class RoeEngineTestCases
{
private EngineManager _game = null;
private const string _windowTitle = "UNITTEST";
[TestFixtureSetUp]
public void SetupTestFixture()
{
_game = new EngineManager(_windowTitle);
EngineManager.Game = _game;
_game.Activated += new EventHandler(game_Activated);
_game.Run();
}
void game_Activated(object sender, EventArgs e)
{
_game.Exit();
}
[TestFixtureTearDown]
public void TeardownTestFixture()
{
_game = null;
}
[Test]
public void GameStartup()
{
Assert.IsNotNull(_game);
Assert.IsNotNull(EngineManager.Game);
Assert.IsNotNull(EngineManager.Device);
Assert.IsNotNull(EngineManager.ContentManager);
Assert.AreEqual(_windowTitle, EngineManager.WindowTitle.ToString());
}
[Test]
public void FpsCounterCreated()
{
Assert.IsNotNull(EngineManager.FpsCounter);
Assert.AreEqual("0", EngineManager.FpsCounter.FPS.ToString());
}
}
}
Finally, we are ready to run our test cases. Open up NUnit and navigate to your bin/Roeengine2UnitTests.dll file. Run the project and voila! Updated… This is what makes the open source community so great. The following was contributed by KaBaL.
using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RoeEngine2.Managers;
using Microsoft.Xna.Framework;
namespace RoeEngine2UnitTest
{
///
/// Summary description for UnitTest1
///
[TestClass]
public class UnitTest1
{
private EngineManager _game = null;
private const string _windowTitle = "UNITTEST";
[TestInitialize()]
public void TestInitialize()
{
_game = new EngineManager(_windowTitle);
EngineManager.Game = _game;
_game.Activated += new EventHandler(game_Activated);
_game.Run();
}
void game_Activated(object sender, EventArgs e)
{
_game.Exit();
}
[TestCleanup()]
public void TestCleanup()
{
_game = null;
}
[TestMethod]
public void GameStartup()
{
Assert.IsNotNull(_game);
Assert.IsNotNull(EngineManager.Game);
Assert.IsNotNull(EngineManager.Device);
Assert.IsNotNull(EngineManager.ContentManager);
//Assert.AreEqual(_windowTitle, EngineManager.WindowTitle.ToString());
}
[TestMethod]
public void FpsCounterCreated()
{
Assert.IsNotNull(EngineManager.FpsCounter);
Assert.AreEqual("0", EngineManager.FpsCounter.FPS.ToString());
}
public UnitTest1()
{
//
// TODO: Add constructor logic here
//
}
}
}
Conclusion
We have just set up a very basic testing environment for our engine. Remember, all of this is optional, TDD may not be for everyone. I strongly urge you to use it, but you know your skillsets better than I.
Please feel free to leave me any comments or suggestions/improvements to the code you see here.
For anyone using Visual Studio Team edition (or those who have hacked VS 2008 to work with XNA) here is the code to use the built in test function.
(roecode – maybe post this as well with code hylighting, since its a common option )
using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RoeEngine2.Manager;
using Microsoft.Xna.Framework;
namespace RoeEngine2UnitTest
{
///
/// Summary description for UnitTest1
///
[TestClass]
public class UnitTest1
{
private EngineManager _game = null;
private const string _windowTitle = “UNITTEST”;
[TestInitialize()]
public void TestInitialize()
{
_game = new EngineManager(_windowTitle);
EngineManager.Game = _game;
_game.Activated += new EventHandler(game_Activated);
_game.Run();
}
void game_Activated(object sender, EventArgs e)
{
_game.Exit();
}
[TestCleanup()]
public void TestCleanup()
{
_game = null;
}
[TestMethod]
public void GameStartup()
{
Assert.IsNotNull(_game);
Assert.IsNotNull(EngineManager.Game);
Assert.IsNotNull(EngineManager.Device);
Assert.IsNotNull(EngineManager.ContentManager);
//Assert.AreEqual(_windowTitle, EngineManager.WindowTitle.ToString());
}
[TestMethod]
public void FpsCounterCreated()
{
Assert.IsNotNull(EngineManager.FpsCounter);
Assert.AreEqual(”0″, EngineManager.FpsCounter.FPS.ToString());
}
public UnitTest1()
{
//
// TODO: Add constructor logic here
//
}
}
}
Nice KaBaL, I will add it to this entry right away.
I wasn’t sure if this was an issue with just my code or not, but I noticed the GameStartup test was failing because the windowTitle wasn’t asserting. Looking into the code, there never was an instance where the WindowTitle was ever being assigned. So I added this to the RoeEngine2 constructor:
protected RoeEngine2(string windowsTitle)
{
_windowTitle = windowsTitle;
// other code
}
That is also why you see that assert commented out in my test code above as well.
Yes, that is true KaBaL, I will have to make a point of cleaning up bugs like this one in my next post.
Mate – absolutely loving your work. I’ll be following this series with much interest!
What’s next?
Thank you jaff. I have a few articles in the works, simple scenegraph, cameras, shaders and textures. Then I will be moving into some more complex items. I am always reading and looking for suggestions.
Hi, how do you import NUnit.Framework into VS C#? as it’s barking about it’s namespace could not be found.
also, The type or namespace name ‘RoeEngine2′ could not be found (are you missing a using directive or an assembly reference?). how do you fix this issue.
Please help as i’m still new, Thank You.
nvm i found that all i need is to add a reference
but i still could not resolve (the type or namespace name ‘RoeEngine2′ could not be found (are you missing a using directive or an assembly reference?)).
thank for any help you can provide.
Btw, nice guide.
nvm…. it’s in the save refrence screen, lol thank’s
Glad you found it, I hope you didn’t mind the lack of response. Some things you just have to discover for yourself to understand.
[...] I: Basic engine skeleton Part II: An FPS counter GameComponent Part III: Applying TDD (Test Driven Development) Part IV: ShaderManager GameComponent Part V: TextureManager GameComponent Part VI: Input [...]
Pingback by Getting up with XNA GS 2.0 [UPDATED] - Kartones Blog | February 19, 2008 |
I’m using NUnit as well, but I want to test the update method of all of my components so this is what I do. Inherit the test fixture as a Game class, and usually each test will call this.Run().
Here’s an example of the FpsCounter test: (I changed some namespaces around)
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using NUnit.Framework;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine.Components;
namespace UnitTests
{
[TestFixture]
public class FpsCounterTest : Microsoft.Xna.Framework.Game
{
private static FpsCounter _fpsCounter = null;
private int loopCounter = 0;
public FpsCounterTest()
{
Initialize();
}
[SetUp]
protected void SetUp()
{
_fpsCounter = new FpsCounter(this);
Components.Add(_fpsCounter);
}
[Test]
public void CounterTest()
{
_fpsCounter.Updated += this.Updated(null, null);
this.Run();
}
private EventHandler Updated(object o1, object o2)
{
return null;
}
#region Inherited Methods
protected override void Initialize()
{
}
protected override void LoadContent()
{
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
_fpsCounter.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
// This is the new, but temporary line of code.
loopCounter++;
if (loopCounter >= 20)
{
Assert.AreEqual(_fpsCounter.FPS, 0);
this.Exit();
}
}
#endregion
}
}
In components, a lot of your code is in the update method, so this should be ran and tested. This will allow you to do that.
Some people can no longer share RoeEngine2 the use of the TestDrien.net experience?
Would like to thank
Great articles. I found NUnit Working great with Visual C# Express Edition. For quick testing just set Tools->External Tools:
Command: …\bin\nunit.exe, Arguments: /run $(ProjectDir)/$(ProjectFileName), Initial Directory: $(ProjectDir)