如何在简单的游戏设置中正确测试Android模型(MVP)?

时间:2017-02-08 19:56:51

标签: android unit-testing mvp

我正在制作一个简单的游戏,并对模型设置/测试有一些疑问!我想尽可能遵循MVP模式。

以下是我的一些模型类的简化版本,主要是那些处理"逻辑"。

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

我对正确测试这些类感兴趣。我可以轻松地测试LocationManagerBattleManager - 它们是分开的,我可以在需要时为代理使用模拟接口(例如LocationManagerDelegate

但是GameManager呢?它在其自己的类中创建具体的LocationManager / BattleManager实例,因此如果我想使用GameManager测试来自类的逻辑的整体流程,它实际上将是测试GameManager以及LocationManager / BattleManager逻辑。我无法找到一种简洁的方法将其拆分......

这样的事情可能更正确吗?

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

然后,无论我们在哪里实际创建GameManager实例,我们都会执行以下操作:

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

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

这将允许我放入模拟LocationManager / BattleManager对象,但可能不会太大,因为这也暴露了一些内部工作" GameManager类的。{它不一定是我需要传递给其他经理类等的公共信息......

那么最好做什么?有任何想法!

1 个答案:

答案 0 :(得分:1)

我想说你有两个选择:

<强> 1。构造函数注入依赖关系

这是你在上面概述的。依赖项在类外部创建,并在创建时传入。这样你就可以在测试时传入你的模拟。如果您使用依赖注入库(如Dagger),也可以这样做。

<强> 2。使依赖关系包专用

由于类的测试与类本身存在于同一个包中,因此它们可以访问包私有变量。在这种情况下,您将更改两个依赖项的访问级别:

public class GameManager implements LocationManagerDelegate {

    LocationManager mLocationManager;
    BattleManager mBattleManager;

    //...
}

然后,从您的测试中,您可以执行以下操作:

@Before
public void setUp() throws Exception {

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

这两种方法都需要权衡开放被测试类的内部。就个人而言,我目前更喜欢使用构造函数注入,因为我已经为DI设置了类。