如果可以模拟一个Game对象来测试我的DrawableGameComponent组件,那我就会徘徊?
我知道模拟框架需要一个接口才能运行,但我需要模拟实际的Game对象。
编辑:以下是关于XNA社区论坛的各个讨论的link。 有什么帮助吗?
答案 0 :(得分:14)
该论坛中有一些关于单元测试主题的好帖子。这是我在XNA中进行单元测试的个人方法:
这是一个测试示例,用于确认我的Update方法将实体移动到Update()调用之间的正确距离。 (我正在使用NUnit。)我用不同的移动向量修剪了几行,但你明白了:你不应该用游戏来推动你的测试。
[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);
}
}
编辑:评论中的其他一些注释:
我的实体类: 我选择将所有游戏对象包装在一个集中的Entity类中,看起来像这样:
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);
}
}
}
这使我可以灵活地创建实体的子类(AIEntity,NonInteractiveEntity ...),它可能会覆盖Update()。它还允许我自由地子类化Drawable,而没有像AnimatedSpriteAIEntity
,ParticleEffectNonInteractiveEntity
和AnimatedSpriteNoninteractiveEntity
这样的n ^ 2子类的地狱。相反,我可以这样做:
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);
我的Drawable类:我有一个抽象类,从中派生出所有绘制的对象。我选择了一个抽象类,因为一些行为将被共享。如果您的代码不正确,那么将其定义为interface是完全可以接受的。
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);
}
子类定义自己的Draw逻辑。在你的坦克示例中,你可以做一些事情:
这是ListDrawable的一个示例实现,忽略了如何管理列表本身的问题。
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);
}
}
}
答案 1 :(得分:3)
MOQ和Rhino Mocks之类的框架并不特别需要接口。他们也可以模拟任何非密封和/或抽象类。游戏是一个抽象类,所以你不应该嘲笑它: - )
至少应该注意这两个框架的一点是,要设置对方法或属性的任何期望,它们必须是虚拟的或抽象的。原因是它生成的模拟实例需要能够覆盖。我相信IAmCodeMonkey提到的类型可以解决这个问题,但我不认为typemock是免费的,而我提到的两个是。
顺便说一句,您还可以查看我的一个项目,该项目可以帮助创建XNA游戏的单元测试而无需进行模拟:http://scurvytest.codeplex.com/
答案 2 :(得分:3)
你不必嘲笑它。为什么不制作假游戏对象?
从Game继承并覆盖您打算在测试中使用的方法,以返回您需要的任何方法/属性的固定值或快捷方式计算。然后把假货传给你的测试。
在嘲笑框架之前,人们会推出他们自己的模拟/存根/假货 - 也许它不是那么快捷,但你仍然可以。
答案 3 :(得分:2)
您可以使用名为TypeMock的工具,我相信不需要您拥有接口。您的另一个更常用的方法是创建一个继承自Game的新类,并实现您创建的与Game对象匹配的接口。然后,您可以针对该接口进行编码并传入“自定义”游戏对象。
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...
}
它有点乏味,但它可以让你实现可模拟性。
答案 4 :(得分:1)
如果你不介意的话,我会小心回到你的帖子,因为mine似乎不那么活跃,你已经把你的代表放在了线上;)
当我阅读你的帖子(这里和XNAForum)时,我认为它既可以更平易近人,也可以是我的(我们的)设计并非完美无缺。
该框架可以设计为更容易扩展。我很难相信Shawn perf hit on interfaces的主要论点。支持我his colleague说可以轻松避开性能 请注意,框架已经具有IUpdatable和IDrawable接口。为什么不一直走?
另一方面,我也认为我的(和你的)设计确实没有完美无瑕。在我不依赖于Game对象的地方,我确实依赖于GraphicsDevice对象。我会看看如何绕过这个。它会让代码变得更复杂,但我认为我确实可以打破这些依赖。
答案 5 :(得分:0)
对于像这样的起点,我会点击XNA WinForms Sample。使用此示例作为模型,似乎在WinForm中可视化组件的一种方法是以与样本中的SpinningTriangleControl相同的样式为其创建控件。这演示了如何在没有Game实例的情况下呈现XNA代码。真正的游戏并不重要,它对你的重要性。因此,您要做的是创建一个库项目,该项目在类和其他项目中具有Component的Load / Draw逻辑,创建一个Control类和一个Component类,它们是各自环境中库代码的包装器。这样,您的测试代码不会重复,您不必担心编写在两种不同情况下始终可行的代码。