Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XNA mock the Game object or decoupling your Game

I was wandering if it's possible to mock a Game object in order to test my DrawableGameComponent component?

I know that mocking frameworks need an interface in order to function, but I need to mock the actual Game object.

edit: Here is a link to respective discussion on XNA Community forums. Any help?

like image 592
Andriy Drozdyuk Avatar asked Apr 30 '09 00:04

Andriy Drozdyuk


4 Answers

There are some good posts in that forum on the topic of unit testing. Here's my personal approach to unit testing in XNA:

  • Ignore the Draw() method
  • Isolate complicated behavior in your own class methods
  • Test the tricky stuff, don't sweat the rest

Here's an example of a test to confirm that my Update method moves Entities the right distance between Update() calls. (I'm using NUnit.) I trimmed out a couple lines with different move vectors, but you get the idea: you shouldn't need a Game to drive your tests.

[TestFixture]
public class EntityTest {
    [Test]
    public void testMovement() {
        float speed = 1.0f; // units per second
        float updateDuration = 1.0f; // seconds
        Vector2 moveVector = new Vector2(0f, 1f);
        Vector2 originalPosition = new Vector2(8f, 12f);

        Entity entity = new Entity("testGuy");
        entity.NextStep = moveVector;
        entity.Position = originalPosition;
        entity.Speed = speed;

        /*** Look ma, no Game! ***/
        entity.Update(updateDuration);

        Vector2 moveVectorDirection = moveVector;
        moveVectorDirection.Normalize();
        Vector2 expected = originalPosition +
            (speed * updateDuration * moveVectorDirection);

        float epsilon = 0.0001f; // using == on floats: bad idea
        Assert.Less(Math.Abs(expected.X - entity.Position.X), epsilon);
        Assert.Less(Math.Abs(expected.Y - entity.Position.Y), epsilon);
    }
}

Edit: Some other notes from the comments:

My Entity Class: I chose to wrap all my game objects up in a centralized Entity class, that looks something like this:

public class Entity {
    public Vector2 Position { get; set; }
    public Drawable Drawable { get; set; }

    public void Update(double seconds) {
        // Entity Update logic...
        if (Drawable != null) {
            Drawable.Update(seconds);
        }
    }

    public void LoadContent(/* I forget the args */) {
        // Entity LoadContent logic...
        if (Drawable != null) {
            Drawable.LoadContent(seconds);
        }
    }
}

This gives me a lot of flexibility to make subclasses of Entity (AIEntity, NonInteractiveEntity...) which probably override Update(). It also lets me subclass Drawable freely, without the hell of n^2 subclasses like AnimatedSpriteAIEntity, ParticleEffectNonInteractiveEntity and AnimatedSpriteNoninteractiveEntity. Instead, I can do this:

Entity torch = new NonInteractiveEntity();
torch.Drawable = new AnimatedSpriteDrawable("Animations\litTorch");
SomeGameScreen.AddEntity(torch);

// let's say you can load an enemy AI script like this
Entity enemy = new AIEntity("AIScritps\hostile");
enemy.Drawable = new AnimatedSpriteDrawable("Animations\ogre");
SomeGameScreen.AddEntity(enemy);

My Drawable class: I have an abstract class from which all my drawn objects are derived. I chose an abstract class because some of the behavior will be shared. It'd be perfectly acceptable to define this as an interface instead, if that's not true of your code.

public abstract class Drawable {
    // my game is 2d, so I use a Point to draw...
    public Point Coordinates { get; set; }
    // But I usually store my game state in a Vector2,
    // so I need a convenient way to convert. If this
    // were an interface, I'd have to write this code everywhere
    public void SetPosition(Vector2 value) {
        Coordinates = new Point((int)value.X, (int)value.Y);
    }

    // This is overridden by subclasses like AnimatedSprite and ParticleEffect
    public abstract void Draw(SpriteBatch spriteBatch, Rectangle visibleArea);
}

The subclasses define their own Draw logic. In your tank example, you could do a few things:

  • Add a new entity for each bullet
  • Make a TankEntity class which defines a List, and overrides Draw() to iterate over the Bullets (which define a Draw method of their own)
  • Make a ListDrawable

Here's an example implementation of ListDrawable, ignoring the question of how to manage the list itself.

public class ListDrawable : Drawable {
    private List<Drawable> Children;
    // ...
    public override void Draw(SpriteBatch spriteBatch, Rectangle visibleArea) {
        if (Children == null) {
            return;
        }

        foreach (Drawable child in children) {
            child.Draw(spriteBatch, visibleArea);
        }
    }
}
like image 186
ojrac Avatar answered Nov 17 '22 20:11

ojrac


frameworks like MOQ and Rhino Mocks don't specifically need an interface. They can mock any non-sealed and/or abstract class as well. Game is an abstract class, so you shouldn't have any trouble mocking it :-)

The one thing to note with at least those two frameworks is that to set any expectations on methods or properties, they must be virtual or abstract. The reason for this is that the mocked instance it generates needs to be able to override. The typemock mentioned by IAmCodeMonkey I believe has a way around this, but I don't think typemock is free, while the two I mentioned are.

As an aside, you can also check out a project of mine that could help in creating unit tests for XNA games without the need to make mocks: http://scurvytest.codeplex.com/

like image 26
Joel Martinez Avatar answered Nov 17 '22 19:11

Joel Martinez


You don't have to mock it. Why not make a fake game object?

Inherit from Game and override the methods you intend to use in your tests to return canned values or shortcut calculations for whatever methods/properties that you need. Then pass the fake to your tests.

Before mocking frameworks, people rolled their own mocks/stubs/fakes - maybe it's not as quick and easy, but you still can.

like image 3
Steven Evers Avatar answered Nov 17 '22 20:11

Steven Evers


You can use a tool called TypeMock that I believe does not require you to have an interface. Your other and more commonly followed method is to create a new class that inherits from Game and also implements an interface that you create that matches the Game object. You can then code against that interface and pass in your 'custom' Game object.

public class MyGameObject : Game, IGame
{
    //you can leave this empty since you are inheriting from Game.    
}

public IGame
{
    public GameComponentCollection Components { get; set; }
    public ContentManager Content { get; set; }
    //etc...
}

Its a bit tedious, but it lets you achieve mockability.

like image 2
IAmCodeMonkey Avatar answered Nov 17 '22 19:11

IAmCodeMonkey