I'm writing WinForms / Xna app and I need some way to abstract away interaction with the GraphicsDevice in my Controller / Model code.
I've created an interface IGraphicsService. I'll use that to abstract away things like loading textures. What I can't figure out though is what to do when I need to return some kind of Texture information. Should I create a class that wraps Texture2D? I'm worried this will incur unnecessary overhead. I would like to be able to create some kind of MockTexture2D in the long run.
This is all so that I can make my app more testable. I'm not so much worried about speed but it would be nice if there was some solution that won't incur to much overhead as eventually I want to use this to make my games more testable. Any suggestions?
My personal opinion is that the GraphicsDevice class is too complex to be mocked and whoever did it despite this would have even more work to actually instruct the mock to do the right thing in his tests (though he should certainly get a medal for the effort :D).
Visual Studio's "Extract Interface" refactoring would only get you half the way there. Maybe a code generator could create a fully forwarding mock. If you worry about performance, as long as your mock interface mirrors the real graphics device, you could do some magic #if..#endif to use the real GraphicsDevice in a Release build and go through the interface in a Debug build?
-
Anyway, for my own unit tests, I'm actually creating a real graphics device on an invisible form for my unit tests. This works surprisingly well, the build agent running my unit tests runs on Windows XP in a VMware virtual machine, with Gentoo Linux x64 as the host OS. I'm testing actual rendering code with shaders and rendertargets and whatnot. Performance-wise, I also can't complain - 1300 tests execute in under 10 seconds.
This is the code I use to create the pseudo-mocked graphics device service: MockedGraphicsDeviceService.cs and its unit test: MockedGraphicsDeviceService.Test.cs
The drawback, of course, is that you cannot check for any expectations in such a pseudo-mocked graphics device (eg. did a call to CreateTexture() occur with a width of 234 and a height 456?). Takes some clever class design to isolate the logic enough so I can test my graphics classes without breaking up their abstraction. At least I can get test coverage on graphics code at all this way :)