TDD (Test-Driven Development) can be great. It encourages you to address edge cases earlier when you have a better grasp of the code you just implemented. It forces you to de-couple classes in ways that allow you to later combine and re-use them in ways that might not have been possible had you developed them using other methodologies.
TDD can also be a big pain-in-the-ass. Isolating logic for testing can be difficult, especially when you need to rely on code that requires complicated external resources: legacy codebases and hardware being the prime examples. The XNA Framework code, with its substantial functionality abstracting the details of hardware on Windows, the Xbox 360, and Windows Phones, falls squarely into both of those categories.
So, does this mean that it’s impossible to use TDD when working with XNA? Hardly. It can be tough, but there are ways to make it easier. In this post, I’ll talk about how I’ve managed to work with XNA while still giving my code the TDD-loving it deserves / needs.
A lot of the power of the XNA Framework comes from several large, core classes: Game, ContentManager, and GraphicsDevice being the major triumvirate. These classes provide the core update, file management and rendering facilities you need to run a game using XNA. They are also fairly intertwined and take a bit of work to instantiate individually. Even if you can manage to include them in your test framework, their instantiation can be quite resource-intensive. This resource-intensiveness can either make your tests unsuitable for rapid iteration (if you instantiate XNA for each test) or harder to keep independent of each other (if you keep XNA instantiated across all tests, thereby allowing the possibility of tests polluting each other with shared state.)
Of course, the real kicker here is that you don’t even need XNA for a lot of your tests. The XNA Framework itself is relatively mature and tested and you couldn’t fix an XNA bug even if you found one because you won’t have source access. The vast majority of code you would want to develop with TDD will have nothing to do with the large XNA classes. So, my big piece of advice for using TDD with XNA is: don’t apply TDD to XNA!
Not touching XNA using TDD is a pragmatic solution although it may not be the methodologically-correct according to the usual tenets of TDD. Theoretically, you could create a mock object to emulate all the functionality of XNA’s GraphicsDevice. However, that would involve a LOT of effort and probably have a lot of bugs in and of itself. For the vast (VAST!) majority of cases, you can entirely ignore GraphicsDevice’s existence and still get the benefits of TDD, even for code related to graphics. In most cases, what you need to test is not the behavior of GraphicsDevice itself, but the data and commands you send to GraphicsDevice.
What this means is that you can apply TDD to code that needs access to complicated XNA classes, like GraphicsDevice and ContentManager, by designing your code to function (in a minimal fashion) given a null instance of the XNA class they require. You’d be surprised at the amount of code you can test even if the class that does all the “real work” is null.
An example from my own code is my TextureLibrary class. It uses ContentManager to load Texture2Ds given a suitable path, but that is only a small (although crucial) part of what TextureLibrary does. Its main mission is to organize collections of Texture2Ds and Rectangles representing UV coordinates for easy access by the rest of the game. The real trick to implementing this class using TDD was realizing that the part of it’s functionality that actually used ContentManager (the loading of textures) was small and trivially simple to implement. By designing TextureLibrary to function (fully except for texture loading proper) with a null ContentManager passed to it, I could concentrate on implementing and testing all the functionality related to adding and removing texture library entries as well as dealing with edge-cases like repeated adds of the same data, etc. When it came to actually using TextureLibrary in a game, I just had to pass in a valid ContentManager and implement the (trivial) code for loading Texture2Ds. Then TextureLibrary just worked there, pretty much exactly as expected.
So there it is, my big tip for how to use TDD with XNA: don’t use TDD with XNA. Or rather, don’t bother trying to get big, complicated XNA classes to work with TDD. You won’t get that much benefit from doing the “right” thing and using mock objects because most of the code you’ll write won’t actually be all that dependent on those big XNA classes. The way I see it, it’s better to test 80% of your code and release a game then to test 100% of your code but never finish a project.