2017-02-08 1 views
0

Je fais un jeu simple et j'ai quelques questions sur l'installation/test du modèle! J'essaie de suivre un modèle MVP si possible.Comment tester correctement le modèle Android (MVP) dans une configuration de jeu simple?

Ce qui suit est une version simplifiée de certaines de mes classes de modèles, principalement celles traitant de la "logique".

public class GameManager implements LocationManagerDelegate { 

    private LocationManager mLocationManager; 
    private BattleManager mBattleManager; 

    public GameManager(Location initialLocation) { 
     mLocationManager = new LocationManager(initialLocation, self); 
     mBattleManager = new BattleManager(); 
    } 

    public void setLocation(Location location) { 
     mLocationManager.setLocation(location); 
    } 

    public void handleLocationAction(Action action) { 
     if (action.type == BATTLE) { 
      mBattleManager.startBattle(); 
     } 
    } 
} 

public class LocationManager { 

    private LocationManagerDelegate mDelegate; 
    private Location mLocation; 

    public LocationManager(Location initialLocation, LocationManagerDelegate delegate) { 
     mLocation = initialLocation; 
     mDelegate = delegate; 
    } 

    public void setLocation(Location location) { 
     mLocation = location; 

     Action action = location.getRandomAction(); 

     mDelegate.handleLocationAction(action); 
    } 
} 

public interface LocationManagerDelegate { 
    void handleLocationAction(Action action); 
} 

public class BattleManager { 

    public BattleManager() { 
    } 

    public void startBattle() { 
     // Do stuff... 
    } 
} 

Je suis intéressé par tester ces classes correctement. Je peux facilement tester LocationManager et BattleManager - ils sont séparés, et je peux utiliser des interfaces simulées pour les délégués en cas de besoin (à savoir LocationManagerDelegate)

Mais qu'en est-GameManager? Il crée des instances concrètes dans sa propre classe, donc si je voulais tester le flux global de la logique à partir des classes utilisant le GameManager, il testera en fait GameManager ainsi que la logique LocationManager/BattleManager. Je ne peux pas trouver un moyen propre de le diviser ...

Peut-être que quelque chose comme ceci est-ce plus correct?

public class GameManager implements LocationManagerDelegate { 

    private ILocationManager mLocationManager; 
    private IBattleManager mBattleManager; 

    public GameManager(Location initialLocation, ILocationManager locationManager, IBattleManager battleManager) { 
     mLocationManager = locationManager; 
     mLocationManager.setInitialLocation(initialLocation); 
     mLocationManager.setDelegate(this); 

     mBattleManager = battleManager; 
     mBattleManager.setDelegate(this); 
    } 

    public void setLocation(Location location) { 
     mLocationManager.setLocation(location); 
    } 

    public void handleLocationAction(Action action) { 
     if (action.type == BATTLE) { 
      mBattleManager.startBattle(); 
     } 
    } 
} 

public interface ILocationManager { 
    void setInitialLocation(Location initialLocation); 
    void setDelegate(LocationManagerDelegate delegate); 
    void setLocation(Location location);  
} 

public class LocationManager implements ILocationManager { 

    private LocationManagerDelegate mDelegate; 
    private Location mLocation; 

    public void setInitialLocation(Location initialLocation) { 
     mLocation = initialLocation; 
    } 

    public void setDelegate(LocationManagerDelegate delegate) { 
     mDelegate = delegate; 
    } 

    public void setLocation(Location location) { 
     mLocation = location; 

     Action action = location.getRandomAction(); 

     mDelegate.handleLocationAction(action); 
    } 
} 

public interface LocationManagerDelegate { 
    void handleLocationAction(Action action); 
} 

public interface IBattleManager { 
    void startBattle(); 
} 

public class BattleManager implements IBattleManager { 

    public void startBattle() { 
     // Do stuff... 
    } 
} 

Ensuite, chaque fois que nous créons en fait l'instance GameManager, nous faisons quelque chose comme:

ILocationManager locationManager = new LocationManager(); 
IBattleManager battleManager = new BattleManager(); 
Location initialLocation = ...; 

GameManager manager = new GameManager(initialLocation, locationManager, battleManager); 

Cela me permettrait de mettre en objets fantaisie LocationManager/BattleManager, mais ne peut pas être trop grand car cela aussi expose certains des «rouages ​​internes» de la classe GameManager. Il ne devrait pas nécessairement être des informations publiques que j'ai besoin de passer dans ces autres classes de gestionnaire, etc ...

Alors, que faire de mieux? Des pensées!

Répondre

1

Je dirais que vous avez deux options:

1. Injection Constructor des dépendances

C'est comme vous schéma ci-dessus. Les dépendances sont créées en dehors de la classe et transmises lors de sa création. De cette façon, vous pouvez passer dans vos simulacres au moment du test. C'est aussi ce que vous feriez si vous utilisiez une bibliothèque d'injection de dépendances telle que Dagger.

2. Faire les dépendances Paquet privé

que les tests pour une classe en direct dans le même package que la classe elle-même, ils ont accès à empaqueter variables privées. Dans ce cas, vous modifiez le niveau d'accès de vos deux dépendances:

public class GameManager implements LocationManagerDelegate { 

    LocationManager mLocationManager; 
    BattleManager mBattleManager; 

    //... 
} 

Ensuite, à partir de votre test, vous êtes en mesure de faire quelque chose comme:

@Before 
public void setUp() throws Exception { 

    gameManager = new GameManager(); 
    gameManager.mLocationManager = locationMock; 
    gameManager.mBattleManager = battleMock; 
} 

Les deux méthodes ont compromis d'ouverture l'intérieur de la classe à tester. Personnellement, je préfère actuellement utiliser l'injection de constructeurs car j'ai déjà mes classes mis en place de cette façon pour DI.