Running on Empty

The few things I know, I like to share.

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.

January 10, 2008 - Posted by roecode | C#, TDD, XBOX360, XNA | | 14 Comments

14 Comments »

  1. 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
    //
    }
    }
    }

    Comment by KaBaL | January 11, 2008 | Reply

  2. Nice KaBaL, I will add it to this entry right away.

    Comment by roecode | January 11, 2008 | Reply

  3. 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. :)

    Comment by KaBaL | January 11, 2008 | Reply

  4. Yes, that is true KaBaL, I will have to make a point of cleaning up bugs like this one in my next post.

    Comment by roecode | January 11, 2008 | Reply

  5. Mate – absolutely loving your work. I’ll be following this series with much interest!
    What’s next? :)

    Comment by jaff | January 13, 2008 | Reply

  6. 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.

    Comment by roecode | January 13, 2008 | Reply

  7. 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.

    Comment by You | January 21, 2008 | Reply

  8. 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.

    Comment by You | January 21, 2008 | Reply

  9. nvm…. it’s in the save refrence screen, lol thank’s

    Comment by You | January 21, 2008 | Reply

  10. 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.

    Comment by roecode | January 25, 2008 | Reply

  11. [...] 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 | Reply

  12. 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.

    Comment by Shane | August 27, 2008 | Reply

  13. Some people can no longer share RoeEngine2 the use of the TestDrien.net experience?
    Would like to thank

    Comment by TianYu | November 17, 2008 | Reply

  14. 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)

    Comment by Arrival | December 29, 2008 | Reply


Leave a comment