Mocking in Scala with Borachio step-by-step

Borachio is now ScalaMock.

Recently, I announced Borachio, native mocking for Scala. This post is a full worked example of using Borachio with ScalaTest and sbt.

The example assumes 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 to hand, which run more quickly than would be the case if we ran on real hardware, and where we can use mocks to simulate errors or other situations difficult to reproduce on demand.

The code for this example is available on GitHub.

  1. Create a directory for our new project:
    $ mkdir mockturtle
    $ cd mockturtle
  2. Create a build definition file called build.sbt containing:
  3. name := "Mock Turtle"
    
    version := "2.0"
    
    scalaVersion := "2.9.1"
    
    libraryDependencies ++= Seq(
      "org.scalatest" %% "scalatest" % "1.6.1" % "test",
      "com.borachio" %% "borachio-scalatest-support" % "latest.integration"
    )
  4. Create src/main/scala/Turtle.scala containing:
    package com.example
    
    trait Turtle {
      def penUp()
      def penDown()
      def forward(distance: Double): (Double, Double)
      def turn(angle: Double)
      def getAngle: Double
      def getPosition: (Double, Double)
    }
  5. The turtle 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. Here’s some code that draws a line from a specific point to another by doing exactly that.

    Create src/main/scala/Controller.scala containing:

    package com.example
    
    import scala.math.{atan2, sqrt}
    
    class Controller(turtle: Turtle) {
    
      def drawLine(start: (Double, Double), end: (Double, Double)) {
        moveTo(start)
    
        val initialAngle = turtle.getAngle
        val deltaPos = delta(start, end)
    
        turtle.turn(angle(deltaPos) - initialAngle)
        turtle.penDown
        turtle.forward(distance(deltaPos))
      }
    
      def delta(pos1: (Double, Double), pos2: (Double, Double)) =
        (pos2._1 - pos1._1, pos2._2 - pos1._2)
    
      def distance(delta: (Double, Double)) =
        sqrt(delta._1 * delta._1 + delta._2 * delta._2)
    
      def angle(delta: (Double, Double)) =
        atan2(delta._2, delta._1)
    
      def moveTo(pos: (Double, Double)) {
        val initialPos = turtle.getPosition
        val initialAngle = turtle.getAngle
    
        val deltaPos = delta(initialPos, pos)
    
        turtle.penUp
        turtle.turn(angle(deltaPos) - initialAngle)
        turtle.forward(distance(deltaPos))
      }
    }
  6. So let’s test that this is doing the right thing. We’ll create a mock Turtle that pretends to start at the (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.

    Create src/test/scala/ControllerTest.scala containing:

    package com.example
    
    import org.scalatest.Suite
    import com.borachio.scalatest.MockFactory
    import scala.math.{Pi, sqrt}
    
    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))
      }
    }
  7. Run the tests with sbt test. You should see “[success]”

So how does this work? First, we create a mock object that implements the Turtle trait, and pass that to an instance of 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” √2. Borachio also supports wildcard parameters (not used here) specified with an * (asterisk).

Finally, we call our code under test with the appropriate arguments:

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

Updated 2011-09-21

Updated for sbt 0.10.x and Borachio 1.3.

3 Responses to “Mocking in Scala with Borachio step-by-step”


  1. 1 Dean Wampler March 5, 2011 at 11:11 pm

    Can you mock classes, as opposed to just traits?

  2. 2 paul March 6, 2011 at 12:03 pm

    Hey Dean,

    No, Borachio doesn’t support this directly at the moment (or companion objects or constructors). Adding direct support would be relatively easy with cglib, but given that my primary goal at the moment is something that works on Android, and Android’s Dalvik VM doesn’t support cglib, it’s not at the top of my todo list. I would certainly be open to accepting contributions, however :-)

    You can go some way towards this by using mock functions – take a look at the OrderTestWithFunctions example in the source.

    Another possibility for the future is a compiler plugin, which would have the benefit of supporting Android, but I’ve got quite a few other things I want to work on before I’ll be able to look at this.

  3. 3 paul July 5, 2011 at 4:34 pm

    Dean,

    I’ve just published an article describing an updated version of Borachio which can mock classes (and singleton objects and constructors):

    http://www.paulbutcher.com/2011/07/power-mocking-in-scala-with-borachio/


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s





Follow

Get every new post delivered to your Inbox.

Join 242 other followers

%d bloggers like this: