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.

Advertisements

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

19 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

  15. Hi,
    Been using these resources and they have been a great help so far.

    VS 2008 Pro (and possibly standard) editions now support XNA and Unit Testing.

    To use the features, make sure you add a new Visual C# > Test > Test Project into your solution, rather than a generic C# class library.

    After you create the test with Kabal’s code, you can use the Test menu (Between tools and window) and select Run > Tests in Current Context (or Run > All Tests in Solution) to initiate the testing procedures.

    Microsoft.VisualStudio.QualityTools should already be added as a reference but I include it just in case. It is important that you create a Test Project, not a generic project or VS will not register the tests.

    Thanks to Roecode for this awesome engine code.

    Ash

    Comment by Ash | July 16, 2009 | Reply

  16. I’m getting this error, I believe this is 64bit related (compiling on 64bit). Set it to x86, but doesn’t work.

    I think you should give some info about installing NUnit aswell, took me 1 hour searching tuts to get that working and understand how it works.

    RoeEngine2UnitTest.RoeEngineTestCases (TestFixtureSetUp):
    System.BadImageFormatException : Could not load file or assembly ‘RoeEngine2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

    Comment by Iltar van der Berg | October 17, 2009 | Reply

  17. That sure sounds like you should take the NUnit classes out, not really used anyway.

    Comment by roecode | October 19, 2009 | Reply

  18. when I try to load the file to the Nunit it fails.
    I get the following error:

    —————————
    Assembly Not Loaded
    —————————
    System.IO.FileNotFoundException : Could not load file or assembly ‘AviadEngineUnitTest’ or one of its dependencies. The system cannot find the file specified.

    For further information, use the Exception Details menu item.
    —————————
    OK
    —————————

    Comment by Aviad | June 9, 2010 | Reply

  19. for those who are in 64bit OS you must run
    nunit-x86.exe

    Comment by Senshi | January 29, 2011 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: