我是否需要创建工厂的测试版以进行单元测试?

时间:2017-09-29 14:24:28

标签: java android unit-testing dependency-injection factory

我用ShipFactory创建了船只对象,但我的船只依赖于我的Acclerometer类(只是android acclerometer的包装器)。因此,我建造一艘船时,我的工厂将加速度计传递给船舶构造商。

这是我的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");

但是现在让我说我要整理我的船级,我想模仿这些依赖。上下文很容易被模拟,因为我可以将模拟上下文传递给我的工厂,但我的工厂仍然依赖于加速度计。

对于单元测试,我应该创建一个仅用于测试的新工厂吗?或者使用工厂的好处是在我的单元测试中我可以一起放弃工厂,并通过将我的模拟传递给船舶构造函数直接创建一艘新船。

3 个答案:

答案 0 :(得分:3)

您的ShipFactory取决于Ship。但Ship不依赖于ShipFactory。独立于Ship测试您的ShipFactory。由于没有依赖性,因此不需要依赖注入。

现在,当您的工厂变大时,您应该专门为您的工厂编写测试。为了实现这一点,我建议在构造函数中提取所有依赖项并注入它们。您可以重载构造函数来帮助您:

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

答案 1 :(得分:2)

在您的代码中,您的Ship类会公开一个公共构造函数,因此您不需要ShipFactory来创建Ship对象。请改用public Ship构造函数并模拟依赖项。

答案 2 :(得分:1)

您可以从ShipFactory中删除显式的Accelerometer依赖项,并像处理Context对象一样将其传入。

1)创建一个AccelerometerFactory,在其构造函数中接受Context,生成Accelerometer

2)修改ShipFactory构造函数以接受Context和Accelerometer,或者您可以修改makeShip方法以接受Accelerometer。

现在您可以单独模拟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");
}