简体   繁体   中英

Do I need to create a test version of my factory for unit testing?

I create ship objects with my ShipFactory, but my ships have a dependency on my Acclerometer class (just a wrapper for the android acclerometer). So I have my factory, when its building a ship, pass in an accelerometer to the ships constructor.

This is my 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;
    }
}

So I make a ship like this:

   ShipFactory shipFactory = new ShipFactory(context);
   ship = shipFactory.makeShip("enemy");

But now lets say I want to unit my ship class and I want to mock out these dependencies. Context is easy to mock because I can just pass a mock context to my factory, but my factory still depends on accelerometer.

For unit testing this should I be creating a new factory just for testing? Or is it a benefit of using a factory that in my unit test I could forgo the factory all together and directly create a new ship by passing my mocks to in the ships constructor.

Your ShipFactory depends on Ship . However Ship does not depend on ShipFactory . Test your Ship independently of the ShipFactory . Since there's no dependency, there is no need for dependency injection.

Now later on when your factory gets bigger, you should write tests specifically for your factory. To accomplish that, I would suggest extracting all of the dependencies and injecting them in the constructor. You can overload the constructor to help you with this:

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

From your code, your Ship class exposes a public constructor so you don't need a ShipFactory to create a Ship object. Use the public Ship constructor instead and mock out the dependencies.

You could remove the explicit Accelerometer dependency from ShipFactory and pass it in as you do with the Context object.

1) Create an AccelerometerFactory which accepts the Context in it's constructor, to generate an Accelerometer

2) Modify your ShipFactory constructor to accept the Context and an Accelerometer, or you could modify the makeShip method to accept the Accelerometer.

Now you can mock both the Context and the Accelerometer separately and pass them to your 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");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM