Menu

Mock objects on Android with Borachio: Part 3

As we saw in part 2 of this series, mocking Android’s PowerManager service directly is impossible. But there is an alternative approach that gives us something close enough. This article describes that approach.

The code of the application described here is checked into GitHub.

Given that we can’t mock PowerManager directly, instead we’re going to create an interface that we can mock:

public interface PowerControl
{
    void disablePowerOff();
    void enablePowerOff();
}

Together with an implementation which will be used in production code:

public class PowerControlImpl implements PowerControl
{
    public PowerControlImpl(Context context) {
        PowerManager powerManager = (PowerManager)
            context.getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(
            PowerManager.FULL_WAKE_LOCK, "PowerControl");
    }

    public void disablePowerOff() {
        wakeLock.acquire();
    }

    public void enablePowerOff() {
        wakeLock.release();
    }

    private PowerManager.WakeLock wakeLock;
}

We won’t be able to test this implementation, but hopefully it’s so simple that (as Hoare puts it) it obviously contains no deficiencies (as opposed to contains no obvious deficiencies).

But we do now have something that we can mock, so we can test that the code that calls it does so correctly.

The first challenge we’re going to have to overcome is how to inject a PowerControl implementation (the real one or the mock) into the code under test. We could use a dependency injection framework like RoboGuice, but for the purposes of this article I’m going to keep things simple and use a custom Application class which implements a getPowerControl method:

public class PowerControlApplication extends Application
{
    public void onCreate() {
        powerControl = new PowerControlImpl(this);
    }

    public PowerControl getPowerControl() {
        return powerControl;
    }

    protected PowerControl powerControl;
}

Our activity calls this during onCreate:

public class PowerActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        PowerControlApplication app =
            (PowerControlApplication)getApplication();
        powerControl = app.getPowerControl();
    }

    public void startImportant(View button) {
        powerControl.disablePowerOff();
    }

    public void stopImportant(View button) {
        powerControl.enablePowerOff();
    }

    private PowerControl powerControl;
}

We can now write a test to verify that startImportant calls disablePowerOff:

class PowerActivityTest
  extends ActivityUnitTestCase[PowerActivity](classOf[PowerActivity])
  with MockFactory {

  val startIntent = new Intent(Intent.ACTION_MAIN)

  def testStartImportant {
    val mockPowerControl = mock[PowerControl]
    val application = new PowerControlApplication {
      powerControl = mockPowerControl
    }
    setApplication(application)
    startActivity(startIntent, null, null)

    withExpectations {
      mockPowerControl expects 'disablePowerOff once

      getActivity.startImportant(null)
    }
  }
}

Our test first creates a mock PowerControl object:

    val mockPowerControl = mock[PowerControl]

And then creates an application object that returns this mock instead of a “real” PowerControl instance:

    val application = new PowerControlApplication {
      powerControl = mockPowerControl
    }

We tell Android’s test framework to use this application object by calling setApplication:

    setApplication(application)

Finally, we set our expectation (that disablePowerOff is called once) and call startImportant:

    mockPowerControl expects 'disablePowerOff once

    getActivity.startImportant(null)

Get Involved

Get exclusive access to pre-release betas and talk to other SwiftKey fans.

VIP Community

@swiftkey