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);
}
}
}