我正在制作一个简单的游戏,并对模型设置/测试有一些疑问!我想尽可能遵循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...
}
}
我对正确测试这些类感兴趣。我可以轻松地测试LocationManager
和BattleManager
- 它们是分开的,我可以在需要时为代理使用模拟接口(例如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
类的。{它不一定是我需要传递给其他经理类等的公共信息......
那么最好做什么?有任何想法!
答案 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设置了类。