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.
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).
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 (*
).
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) }
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)
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/
Have an opinion? Readers have already posted 10 comments about this article. Why not add yours?
-
Artima provides consulting and training services to help you make the most of Scala, reactive
and functional programming, enterprise systems, big data, and testing.