The Artima Developer Community
Sponsored Link

Scalazine
Borachio: Mock Objects for Scala and Android
by Paul Butcher
July 1, 2011

Advertisement

Summary
This article describes Borachio, a mock object library for Scala. Unlike the popular Java mock object libraries, Borachio does not require cglib and can therefore be used to test Android applications. In addition, unlike its Java cousins, Borachio supports both mock objects and mock functions.

Borachio is a mock object library for Scala. Why create a new mocking library when there are already so many excellent Java mocking libraries? Because existing Java libraries:

This article will show how Borachio supports both mock objects and mock functions, as well as how it can be used to test Android applications. We'll start with mock objects.

Mock Objects

Imagine that we’re writing code to control a mechanical turtle, similar to that used by Logo programs. Mocking is useful in this kind of situation because we might want to create tests that function even if we don’t have the hardware at hand. The tests might run more quickly than would be the case if we ran on real hardware, and we could use mocks to simulate errors or other situations difficult to reproduce on demand.

Here’s the Turtle API:

trait Turtle {
  def penUp()
  def penDown()
  def forward(distance: Double)
  def turn(angle: Double)
  def getAngle: Double
  def getPosition: (Double, Double)
}

This API is not very convenient. We have no way to move to a specific position, instead we need to work out how to get from where we are now to where we want to get by calculating angles and distances. The diagram below, for example, demonstrates the movements a turtle starting at the origin (0, 0) would need to make to draw a line from (1, 1) to (2, 1).

Turtle diagram

You can see an example of code that performs these calculations here. This isn’t trivial, so we want to test to make sure that it’s doing the right thing. Here’s a test (written with ScalaTest) that creates a mock turtle that pretends to start at the origin (0, 0) and verifies that if we ask the code we’ve just written to draw a line from (1, 1) to (2, 1) it performs the correct sequence of turns and movements:

class MockFunctionTest extends Suite with MockFactory {

  val mockTurtle = mock[Turtle]
  val controller = new Controller(mockTurtle)

  def testDrawLine() {
    inSequence {
      mockTurtle expects 'getPosition returning (0.0, 0.0)
      mockTurtle expects 'getAngle returning 0.0
      mockTurtle expects 'penUp
      mockTurtle expects 'turn withArgs (~(Pi / 4))
      mockTurtle expects 'forward withArgs (~sqrt(2.0))
      mockTurtle expects 'getAngle returning Pi / 4
      mockTurtle expects 'turn withArgs (~(-Pi / 4))
      mockTurtle expects 'penDown
      mockTurtle expects 'forward withArgs (1.0)
    }

    controller.drawLine((1.0, 1.0), (2.0, 1.0))
  }
}

So how does this work? First, we create a mock object that implements the Turtle trait, and pass that to an instance of the Controller that we’ll test later:

  val mockTurtle = mock[Turtle]
  val controller = new Controller(mockTurtle)

Then, in our test, we start by setting up what we expect to happen. In this case, ordering is important, so we ensure that our functions are called in order using inSequence:

    inSequence {
      // expectations
    }

We list which methods we expect to be called, together with their arguments. In addition, where it’s important for the functionality we’re testing, we also specify the values that our mock object should return.

There’s a wrinkle, however, because we’re dealing with floating-point numbers. If we test for simple equality, rounding errors are likely to stop our tests from passing. That’s where the ~ (tilde) operator comes in:

      mockTurtle expects 'forward withArgs (~sqrt(2.0))

This says that we expect the forward method to be called with a single argument which is “close to” the square root of 2. Borachio also supports wildcard parameters (not used here) specified with an asterisk (*).

Mock Functions

Being a hybrid object/functional language, functions are first-class objects in Scala. Borachio makes mocking functions just as easy as mocking objects. Here, for example, is a test that confirms that the foldLeft function in the Scala standard library behaves as expected:

def testFoldLeft() {
  val f = mockFunction[String, Int, String]

  f expects ("initial", 0) returning "intermediate one"
  f expects ("intermediate one", 1) returning "intermediate two"
  f expects ("intermediate two", 2) returning "intermediate three"
  f expects ("intermediate three", 3) returning "final"

  expect("final") { Seq(0, 1, 2, 3).foldLeft("initial")(f) }
}

First, we create a mock function with mockFunction, which declares its argument types in a similar manner to Scala’s Function trait:

  val f = mockFunction[String, Int, String]

This creates a mock function that takes two arguments, a String and an Int, and returns a String.

Then we set expectations on f in a similar manner to the mock objects we’ve already seen (the only difference being that we don’t need to specify the name of the method).

Finally, we create a sequence and call its foldLeft member:

  expect("final") { Seq(0, 1, 2, 3).foldLeft("initial")(f) }

Mock Objects on Android

Because Borachio is written in pure Scala without code generation, it works just fine on Android, but you’ll need to write your tests in Scala. Borachio can, nevertheless, be used to test code written in Java.

Android’s API design can occasionally make mocking OS services challenging, but it can be done. The example below shows how we can write a simple test of an application that uses Android’s PowerManager service. PowerManager allows us to control when the device switches on or off.

Borachio, in common with most other mocking frameworks, can only mock interfaces. And PowerManager is a class, not an interface, so we can’t mock it directly. In addition, its constructor is private, so neither can we derive from PowerManager and just mock the methods we’re interested in. The solution, therefore, is to create an interface that we can mock:

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

Together with an implementation that 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 Android Application class that implements a getPowerControl method:

import android.app.Application;

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

    public PowerControl getPowerControl() {
        return powerControl;
    }

    protected PowerControl powerControl;
}

Our activity (in Android, an activity is an application component that provides a screen with which users can interact) calls this during onCreate:

import android.app.Activity;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.View;

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)

Share your opinion

What is your opinion on Borachio's approach and syntax for mocking objects? How useful do you think it will be to be able to mock functions? To what extent would Borachio's current inability to mock classes be a problem for you? Discuss this article in the Articles Forum topic, Borachio: Mock Objects for Scala and Android.

Resources

Borachio can be found at:
http://www.borachio.com/

cglib can be found at:
http://cglib.sourceforge.net/

wikipedia for Logo can be found at:
http://en.wikipedia.org/wiki/Logo_%28programming_language%29

Mock Turtle can be found at:
http://github.com/paulbutcher/mockturtle/

Scala can be found at:
http://www.scala-lang.org/

ScalaTest can be found at:
http://www.scalatest.org/

Android PowerManager service can be found at:
http://developer.android.com/reference/android/os/PowerManager.html

C.A.R Hoare can be found at:
http://en.wikipedia.org/wiki/C._A._R._Hoare

RoboGuice can be found at:
"http://code.google.com/p/roboguice/

About the author

Paul Butcher is the Chief Software Architect at TouchType, author of Debug It! published by The Pragmatic Bookshelf, and the lead developer of Borachio.



Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us