javaandroidunit-testingdependency-injectionfactory

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 Accelerometer class (just a wrapper for the Android accelerometer). So I have my factory, when it's 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 let's 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?


Solution

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