Question

Je me demandais s'il était possible de se moquer d'un objet Game pour tester mon composant DrawableGameComponent?

Je sais que les frameworks moqueurs ont besoin d’une interface pour fonctionner, mais je dois me moquer de la Objet du jeu.

modifier: voici un lien vers la discussion correspondante sur les forums de la communauté XNA. . Toute aide?

Était-ce utile?

La solution

Ce forum contient de bons articles sur les tests unitaires. Voici mon approche personnelle des tests unitaires sous XNA:

  • Ignorer la méthode Draw ()
  • Isolez le comportement compliqué dans vos propres méthodes de classe
  • Testez les trucs délicats, ne vous laissez pas transpirer

Voici un exemple de test pour confirmer que ma méthode de mise à jour déplace les entités de la bonne distance entre les appels Update (). (J'utilise NUnit .) J'ai découpé quelques lignes avec différents vecteurs de déplacement, mais vous avez l’idée: vous ne devriez pas avoir besoin d’un jeu pour piloter vos 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);
    }
}

Édition: quelques autres notes tirées des commentaires:

Ma classe d'entité : J'ai choisi de regrouper tous mes objets de jeu dans une classe d'entité centralisée, qui ressemble à ceci:

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

Cela me donne beaucoup de flexibilité pour créer des sous-classes de Entity (AIEntity, NonInteractiveEntity ...) qui écrasent probablement Update (). Cela me permet également de sous-classe Drawable librement, sans l'enfer de n ^ 2 sous-classes telles que AnimatedSpriteAIEntity , ParticleEffectNonInteractiveEntity et AnimatedSpriteNoninteractiveEntity . Au lieu de cela, je peux faire ceci:

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

Ma classe Drawable : j'ai une classe abstraite à partir de laquelle tous mes objets dessinés sont dérivés. J'ai choisi une classe abstraite car certains comportements seront partagés. Il serait parfaitement acceptable de définir cela comme une interface à la place, si ce n'est pas le cas de votre 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);
}

Les sous-classes définissent leur propre logique Draw. Dans votre exemple de tank, vous pouvez faire quelques choses:

  • Ajouter une nouvelle entité pour chaque puce
  • Crée une classe TankEntity qui définit une liste et substitue Draw () pour itérer sur les puces (définissant une méthode Draw propre)
  • Créer un ListDrawable

Voici un exemple d'implémentation de ListDrawable, ignorant la question de savoir comment gérer la liste elle-même.

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

Autres conseils

des cadres tels que MOQ et Rhino Mocks n’a pas spécifiquement besoin d’une interface. Ils peuvent également se moquer de toute classe abstraite ou non scellée. Le jeu est une classe abstraite, vous ne devriez donc avoir aucune difficulté à vous en moquer: -)

La seule chose à noter avec au moins ces deux cadres est que, pour définir les attentes relatives aux méthodes ou aux propriétés, elles doivent être virtuelles ou abstraites. La raison en est que l'instance simulée qu'elle génère doit pouvoir être remplacée. Le code de frappe mentionné par IAmCodeMonkey a, je crois, un moyen de contourner cela, mais je ne pense pas que le code de frappe soit gratuit, alors que les deux que j'ai mentionnés le sont.

En passant, vous pouvez également consulter un projet mien qui pourrait vous aider à créer des tests unitaires pour les jeux XNA sans avoir à simuler: http://scurvytest.codeplex.com/

Vous n'avez pas à vous moquer de ça. Pourquoi ne pas créer un faux objet de jeu?

Héritez de Game et remplacez les méthodes que vous avez l'intention d'utiliser dans vos tests pour renvoyer des valeurs fixes ou des calculs de raccourcis pour les méthodes / propriétés dont vous avez besoin. Puis passez le faux à vos tests.

Avant de se moquer des cadres, les gens fabriquaient leurs propres imitations / bases / faux - peut-être que ce n'était pas aussi rapide et facile, mais vous le pouvez toujours.

Vous pouvez utiliser un outil appelé TypeMock qui, à mon avis, ne nécessite pas d’interface. Votre autre méthode, plus couramment suivie, consiste à créer une nouvelle classe qui hérite de Game et implémente également une interface que vous créez qui correspond à l'objet Game. Vous pouvez ensuite coder contre cette interface et transmettre votre objet de jeu "personnalisé".

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...
}

C'est un peu fastidieux, mais cela vous permet d'atteindre la mocabilité.

Je vais reprendre votre message si cela ne vous dérange pas, car mine semble être moins actif et vous avez déjà mis votre représentant sur la ligne;)

En lisant vos messages (ici et sur XNAForum), je pense que c’est à la fois le cadre qui pourrait être plus accessible et mon (notre) design qui n’est pas sans faille.

Le cadre aurait pu être conçu pour être plus facile à étendre. J'ai du mal à croire les Shawn argument principal d'un performances des interfaces . Pour me sauvegarder, son collègue indique que le succès pourrait être facilement évité.
Notez que le framework a déjà une interface IUpdatable et IDrawable. Pourquoi ne pas aller jusqu'au bout?

D'autre part, je pense aussi que mon design (et le vôtre) ne sont pas parfaits. Lorsque je ne dépend pas de l'objet Game, je dépend beaucoup de l'objet GraphicsDevice. Je vais regarder comment je peux contourner ça. Cela va rendre le code plus compliqué, mais je pense que je peux vraiment briser ces dépendances.

Pour un point de départ sur quelque chose comme ceci, je voudrais aller à la exemple XNA WinForms . En utilisant cet exemple comme modèle, il apparaît qu'une façon de visualiser les composants dans un formulaire WinForm consiste à créer un contrôle pour celui-ci dans le même style que le SpinningTriangleControl de l'exemple. Cela montre comment rendre le code XNA sans instance de jeu. Vraiment le jeu n'est pas important, c'est ce qu'il fait pour vous qui compte. Vous feriez donc créer un projet de bibliothèque contenant la logique de chargement / dessin du composant dans une classe et dans vos autres projets, créez une classe Control et une classe de composant qui encapsulent le code de la bibliothèque dans leurs environnements respectifs. Ainsi, le code que vous testez n'est pas dupliqué et vous n'avez pas à vous soucier d'écrire du code qui sera toujours viable dans deux scénarios différents.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top