Xna: Mocking Texture2D

落花浮王杯 提交于 2019-12-23 14:24:15

问题


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?


回答1:


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




回答2:


You could try to use Scurvy.Test to write your unit tests. That way, you don't need to mock up the graphics device at all, just use the actual instance :-)

http://scurvytest.codeplex.com

disclaimer: I'm the author of that lib




回答3:


I hit this problem as well when trying to use dummy textures in a unit test. This is how i got around it:

internal class SneakyTexture2D : Texture2D
{
    private static readonly object Lockobj = new object();
    private static volatile Texture2D instance;

    private SneakyTexture2D()
        : this(() => throw new Exception())
    {
    }

    private SneakyTexture2D(Func<GraphicsDevice> func)
        : this(func())
    {
    }

    // Is never called
    private SneakyTexture2D(GraphicsDevice g)
        : base(g, 0, 0)
    {
    }

    // INTENTIONAL MEMORY LEAK AHOY!!!
    ~SneakyTexture2D()
    {
        instance = this;
    }


    // This is the actual "constructor"
    public static Texture2D CreateNamed(string name)
    {
        lock (Lockobj)
        {
            Texture2D local;
            instance = null;
            while ((local = instance) == null)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();

                GC.WaitForFullGCComplete();
                try
                {
                    DoNotUseMe = new SneakyTexture2D();
                }
                catch
                {
                }
            }

            local.Name = name;
            return local;
        }
    }
}

The SneakyTexture2D Class, while extending from Texture2D, gets away with not calling/initializing the base object constructor and will therefore not require a Graphicsdevice.

The fact that i am using the Name property on the Texture is a bit of a gamble. In general interacting with an uninitialized object is a bit dangerous.

Huge shout-out to Jon Skeets brilliant article on the subject:https://codeblog.jonskeet.uk/2014/10/23/violating-the-smart-enum-pattern-in-c/



来源:https://stackoverflow.com/questions/1308560/xna-mocking-texture2d

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!