我需要为我的工厂创建一个测试版本来进行单元测试吗?
Do I need to create a test version of my factory for unit testing?
我使用我的 ShipFactory 创建船舶对象,但我的船舶依赖于我的加速度计 class(只是 android 加速度计的包装器)。所以我有我的工厂,当它建造一艘船时,将加速度计传递给船舶建造者。
这是我的 ShipFactory:
public class ShipFactory {
private int screenX;
private int screenY;
private Context context;
private Bitmap bitmap;
// How can I mock this from in my factory?
private Accelerometer accel;
private Ship ship;
public ShipFactory(Context context){
this.context = context;
accel = new Accelerometer(context);
}
public Ship makeShip(String shipType){
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.myship)
ship = new Ship(context,screenX,screenY,bitmap,accel);
return ship;
}
}
所以我做了这样一艘船:
ShipFactory shipFactory = new ShipFactory(context);
ship = shipFactory.makeShip("enemy");
但是现在假设我想将我的船 class 组合起来,并且我想模拟这些依赖项。上下文很容易模拟,因为我可以将模拟上下文传递给我的工厂,但我的工厂仍然依赖于加速度计。
对于单元测试,我应该创建一个仅用于测试的新工厂吗?或者使用工厂的好处是在我的单元测试中我可以完全放弃工厂并通过将我的模拟传递给船舶构造函数来直接创建一艘新船。
根据您的代码,您的 Ship
class 公开了一个 public 构造函数,因此您不需要 ShipFactory
来创建 Ship
对象。请改用 public Ship
构造函数并模拟依赖项。
您可以从 ShipFactory 中删除显式加速度计依赖项,并像处理 Context 对象一样将其传入。
1) 创建一个在其构造函数中接受 Context 的 AccelerometerFactory,以生成一个 Accelerometer
2) 修改您的 ShipFactory 构造函数以接受上下文和加速度计,或者您可以修改 makeShip 方法以接受加速度计。
现在您可以分别模拟 Context 和 Accelerometer 并将它们传递给您的 ShipFactory。
public class AccelerometerFactory {
private Context context;
public AccelerometerFactory(Context context){
this.context = context;
}
public Accelerometer makeAccelerometer(){
return new Accelerometer(context);
}
}
public class ShipFactory {
private int screenX;
private int screenY;
private Context context;
private Bitmap bitmap;
private Accelerometer accel;
private Ship ship;
public ShipFactory(Context context, Accelerometer accelerometer){
this.context = context;
this.accel = accelerometer;
}
public Ship makeShip(String shipType){
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.myship)
ship = new Ship(context,screenX,screenY,bitmap,accel);
return ship;
}
}
{// Calling code
AccelerometerFactory accelFactory = new AccelerometerFactory(context);
Accelerometer accel = accelFactory.makeAccelerometer(); // Note: Accelerometer class would have to be accessible here, not sure if this is the case for you
ShipFactory shipFactory = new ShipFactory(context, accel);
ship = shipFactory.makeShip("enemy");
}
您的 ShipFactory
取决于 Ship
。但是 Ship
不依赖于 ShipFactory
。独立于 ShipFactory
测试你的 Ship
。因为没有依赖,所以不需要依赖注入。
以后你的工厂做大了,你应该专门为你的工厂写测试。为此,我建议提取所有依赖项并在构造函数中 注入 它们。您可以重载构造函数来帮助您:
// you can use this for convenience
public ShipFactory(Context context){
this(new BitmapProvider(context), new Accelerometer(context));
}
// use this for testing because you can provide mock versions
public ShipFactory(BitmapProvider provider, Accelerometer accel){
this.provider = provider;
this.accel = accel;
}
// wrapping BitmapFactory because it is a buncha static methods... aka a pain to mock
class BitmapProvider {
Context context;
public BitmapProvider(Context context){
this.context = context;
}
public Bitmap getBitmap(int resId){
return BitmapFactory.decodeResource(context.getResources(), resId);
}
}
我使用我的 ShipFactory 创建船舶对象,但我的船舶依赖于我的加速度计 class(只是 android 加速度计的包装器)。所以我有我的工厂,当它建造一艘船时,将加速度计传递给船舶建造者。
这是我的 ShipFactory:
public class ShipFactory {
private int screenX;
private int screenY;
private Context context;
private Bitmap bitmap;
// How can I mock this from in my factory?
private Accelerometer accel;
private Ship ship;
public ShipFactory(Context context){
this.context = context;
accel = new Accelerometer(context);
}
public Ship makeShip(String shipType){
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.myship)
ship = new Ship(context,screenX,screenY,bitmap,accel);
return ship;
}
}
所以我做了这样一艘船:
ShipFactory shipFactory = new ShipFactory(context);
ship = shipFactory.makeShip("enemy");
但是现在假设我想将我的船 class 组合起来,并且我想模拟这些依赖项。上下文很容易模拟,因为我可以将模拟上下文传递给我的工厂,但我的工厂仍然依赖于加速度计。
对于单元测试,我应该创建一个仅用于测试的新工厂吗?或者使用工厂的好处是在我的单元测试中我可以完全放弃工厂并通过将我的模拟传递给船舶构造函数来直接创建一艘新船。
根据您的代码,您的 Ship
class 公开了一个 public 构造函数,因此您不需要 ShipFactory
来创建 Ship
对象。请改用 public Ship
构造函数并模拟依赖项。
您可以从 ShipFactory 中删除显式加速度计依赖项,并像处理 Context 对象一样将其传入。
1) 创建一个在其构造函数中接受 Context 的 AccelerometerFactory,以生成一个 Accelerometer
2) 修改您的 ShipFactory 构造函数以接受上下文和加速度计,或者您可以修改 makeShip 方法以接受加速度计。
现在您可以分别模拟 Context 和 Accelerometer 并将它们传递给您的 ShipFactory。
public class AccelerometerFactory {
private Context context;
public AccelerometerFactory(Context context){
this.context = context;
}
public Accelerometer makeAccelerometer(){
return new Accelerometer(context);
}
}
public class ShipFactory {
private int screenX;
private int screenY;
private Context context;
private Bitmap bitmap;
private Accelerometer accel;
private Ship ship;
public ShipFactory(Context context, Accelerometer accelerometer){
this.context = context;
this.accel = accelerometer;
}
public Ship makeShip(String shipType){
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.myship)
ship = new Ship(context,screenX,screenY,bitmap,accel);
return ship;
}
}
{// Calling code
AccelerometerFactory accelFactory = new AccelerometerFactory(context);
Accelerometer accel = accelFactory.makeAccelerometer(); // Note: Accelerometer class would have to be accessible here, not sure if this is the case for you
ShipFactory shipFactory = new ShipFactory(context, accel);
ship = shipFactory.makeShip("enemy");
}
您的 ShipFactory
取决于 Ship
。但是 Ship
不依赖于 ShipFactory
。独立于 ShipFactory
测试你的 Ship
。因为没有依赖,所以不需要依赖注入。
以后你的工厂做大了,你应该专门为你的工厂写测试。为此,我建议提取所有依赖项并在构造函数中 注入 它们。您可以重载构造函数来帮助您:
// you can use this for convenience
public ShipFactory(Context context){
this(new BitmapProvider(context), new Accelerometer(context));
}
// use this for testing because you can provide mock versions
public ShipFactory(BitmapProvider provider, Accelerometer accel){
this.provider = provider;
this.accel = accel;
}
// wrapping BitmapFactory because it is a buncha static methods... aka a pain to mock
class BitmapProvider {
Context context;
public BitmapProvider(Context context){
this.context = context;
}
public Bitmap getBitmap(int resId){
return BitmapFactory.decodeResource(context.getResources(), resId);
}
}