ScalaMock step-by-step

This post describes ScalaMock 2, which is no longer under development. For the current development version, go here.

ScalaMock (previously Borachio) is a mocking library for Scala. As well as traits (interfaces) and functions, it can also mock:

  • Classes
  • Singleton and companion objects (static methods)
  • Object creation (constructor invocation)
  • Classes with private constructors
  • Final classes and classes with final methods
  • Operators (methods with symbolic names)
  • Overloaded methods

This post describes how to setup a project that uses ScalaMock in conjunction with ScalaTest and sbt 0.11. The sample code described in this article is available on GitHub.

Note: If you only want to mock functions and traits (interfaces), you can use ScalaMock’s proxy mocks by simply linking with the .jar file – no need to use the compiler plugin or sbt plugin described below.

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.

Getting started

  1. Create a root directory for your project:
    $ mkdir myproject
  2. ScalaMock uses a compiler plugin to generate code. There’s an sbt plugin that makes setting this up easy. Add this plugin to your project by creating project/project/Build.scala containing:
    import sbt._
    object PluginDef extends Build {
      override lazy val projects = Seq(root)
      lazy val root = Project("plugins", file(".")) dependsOn(scalamockPlugin)
      lazy val scalamockPlugin = uri("git://github.com/paulbutcher/scalamock-sbt-plugin")
    }
  3. Create project/Build.scala containing:
    import sbt._
    import Keys._
    import ScalaMockPlugin._
    
    object MyBuild extends Build {
    
      override lazy val settings = super.settings ++ Seq(
        organization := "com.example",
        version := "1.0",
        scalaVersion := "2.9.1",
    
        resolvers += ScalaToolsSnapshots,
        libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "2.0-SNAPSHOT",
        autoCompilerPlugins := true,
        addCompilerPlugin("org.scalamock" %% "scalamock-compiler-plugin" % "2.0-SNAPSHOT"))
    
      lazy val myproject = Project("MyProject", file(".")) settings(generateMocksSettings: _*) configs(Mock)
    }

Simple mocking

  1. Now we’ve got a project, we need some code to test. Let’s start with a simple trait representing a turtle. Create src/main/scala/Turtle.scala containing:
    package com.example
    
    trait Turtle {
      def penDown()
      def penUp()
      def forward(distance: Double)
      def turn(angle: Double)
      def getPosition: (Double, Double)
      def getAngle: Double
    }
  2. 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))
      }
    }
  3. In order to create mock turtles, we need to tell ScalaMock to generate the appropriate code by using the @mock annotation. Create src/generate-mocks/scala/GenerateMocks.scala containing:
    package com.example
    
    import org.scalamock.annotation.mock
    
    @mock[Turtle]
    class Dummy
  4. We can now write a test. We’ll create a mock turtle that pretends to start at the origin (0, 0) and verifies that if we draw a line from (1, 1) to (2, 1) it performs the correct sequence of turns and movements.

    Turtle diagram

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

    package com.example
    
    import org.scalatest.FunSuite
    import org.scalamock.scalatest.MockFactory
    import org.scalamock.generated.GeneratedMockFactory
    import scala.math.{Pi, sqrt}
    
    class ControllerTest extends FunSuite with MockFactory with GeneratedMockFactory {
    
      test("draw line") {
        val mockTurtle = mock[Turtle]
        val controller = new Controller(mockTurtle)
    
        inSequence {
          inAnyOrder {
            mockTurtle.expects.penUp
            mockTurtle.expects.getPosition returning (0.0, 0.0)
            mockTurtle.expects.getAngle returning 0.0
          }
          mockTurtle.expects.turn(~(Pi / 4))
          mockTurtle.expects.forward(~sqrt(2.0))
          mockTurtle.expects.getAngle returning Pi / 4
          mockTurtle.expects.turn(~(-Pi / 4))
          mockTurtle.expects.penDown
          mockTurtle.expects.forward(1.0)
        }
    
        controller.drawLine((1.0, 1.0), (2.0, 1.0))
      }
    }

    This should (hopefully!) be self-explanatory, with one possible exception. The tilde (~) operator represents an epsilon match, useful for taking account of rounding errors when dealing with floating-point values.

  5. Generate mocks with generate-mocks and then run the tests with test:
    $ sbt
    > generate-mocks
    [log generatemocks] Creating mock for: trait Turtle
    > test
    [info] ControllerTest:
    [info] - draw line
    [info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0

Getting clever: Constructors and singleton objects

  1. Turtles use ink. Let’s define a singleton object that keeps track of how much we’ve used.

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

    package com.example
    
    object InkReservoir {
    
      def use(r: Double, g: Double, b: Double) {
        red -= r
        green -= g
        blue -= b
      }
    
      private var red = 10.0
      private var green = 10.0
      private var blue = 10.0
    }
  2. And now let’s create a concrete implementation of our Turtle trait, which adds the ability to define a pen colour, together with a factory method implemented in its companion object.

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

    package com.example
    
    import scala.math.{cos, sin}
    
    class ColouredTurtle(r: Double, g: Double, b: Double) extends Turtle {
      def penDown() { penIsDown = true }
      def penUp() { penIsDown = false }
      def turn(angle: Double) { theta += angle}
      def getPosition: (Double, Double) = (x, y)
      def getAngle: Double = theta
      def forward(d: Double) {
        x += sin(theta) * d
        y += cos(theta) * d
        if (penIsDown)
          InkReservoir.use(r * d, g * d, b * d)
      }
    
      private var penIsDown = false
      private var x = 0.0
      private var y = 0.0
      private var theta = 0.0
    }
    
    object ColouredTurtle {
    
      def apply(colour: Symbol) = {
        val (r, g, b) = colourMap(colour)
        new ColouredTurtle(r, g, b)
      }
    
      private val colourMap = Map('red -> (1.0, 0.0, 0.0), 'green -> (0.0, 1.0, 0.0), 'blue -> (0.0, 0.0, 1.0))
    }
  3. To mock ColouredTurtle, first we need to add another @mock annotation to GenerateMocks.scala:
    @mock[ColouredTurtle]
  4. We can now write a test to verify that our factory method works.

    Create src/test/scala/ColouredTurtleTest.scala:

    package com.example
    
    import org.scalatest.FunSuite
    import org.scalamock.scalatest.MockFactory
    import org.scalamock.generated.GeneratedMockFactory
    
    class ColouredTurtleTest extends FunSuite with MockFactory with GeneratedMockFactory {
    
      test("coloured turtles") {
        val m1 = mock[ColouredTurtle]
        val m2 = mock[ColouredTurtle]
    
        m1.expects.newInstance(1.0, 0.0, 0.0)
        m2.expects.newInstance(0.0, 1.0, 0.0)
        m2.expects.forward(3.0)
    
        val t1 = ColouredTurtle('red)
        val t2 = ColouredTurtle('green)
        t2.forward(3.0)
      }
    }
  5. Run the tests again (don’t forget to run generate-mocks first) to see:
    [info] ColouredTurtleTest:
    [info] - coloured turtles
    [info] ControllerTest:
    [info] - draw line
    [info] Passed: : Total 2, Failed 0, Errors 0, Passed 2, Skipped 0
  6. Finally, we can add a test to verify that we’re keeping track of ink correctly. Let ScalaMock know that we’ll be mocking InkReservoir by adding a @mockObject annotation:
    @mockObject(InkReservoir)</pre>
    
    And add a test to <code>ColouredTurtleTest.scala</code>:
    
    <pre>  test("ink reservoir") {
        val m = mockObject(InkReservoir)
    
        m.expects.use(0.0, 3.0, 0.0)
    
        val t = ColouredTurtle('green)
        t.penDown
        t.forward(3.0)
      }
  7. [info] ControllerTest:
    [info] - draw line
    [info] ColouredTurtleTest:
    [info] - coloured turtles
    [info] - ink reservoir
    [info] Passed: : Total 3, Failed 0, Errors 0, Passed 3, Skipped 0

12 Responses to “ScalaMock step-by-step”


  1. 1 Rupert November 21, 2011 at 8:23 pm

    Hi, Paul
    First up, thanks for all this work! Borachio/ScalaMock is the answer I’ve been looking for (… I hope πŸ˜‰ ) and SwiftKey’s super.

    Right, ’nuff sucking up — now the question: Android (+Eclipse|Ant)?

    In your post “β€œPower” mocking in Scala with Borachio” http://www.paulbutcher.com/2011/07/power-mocking-in-scala-with-borachio/ you concluded “Android and JUnit3 integration” in your “To do” section. What’s the status on Android use now? Ideally for Eclipse users but at least for Ant? I found scalamock-junit3-support so at least partially good!

    I see from your response re. Gradle…
    https://groups.google.com/d/msg/scala-user/IZAouFY6N9U/_ntbRbntyv8J
    …that we might be doing without mocking ‘objects’. Does this include interfaces? (yeah, I’m trying to resurrect my rusty Scala. Sorry if this doesn’t make much sense.)
    As background, I started using Borachio last week to test Java Android code like https://github.com/jaley/borachio-warehouse then realised ScalaMock might be ready!

    Cheers, Rupert.

  2. 2 paul November 22, 2011 at 11:30 am

    Thanks for the kind comments, Rupert.

    ScalaMock on Android is still a work in progress. I’ve thought that I’ve got it working on a couple of occasions but Android has managed to find a way to throw a spanner in the works each time. I’m currently lost in a twisty maze of ClassLoaders :-/ I continue to work on it, and don’t plan to be defeated, however πŸ™‚

    Having said all that, ScalaMock is a strict superset of Borachio, so you should be able to use it on Android just like Borachio. But you’ll only be able to use proxy mocks (i.e. you’ll only be able to mock interfaces).

  3. 3 Kostas Kougios December 21, 2011 at 11:49 am

    Hi Paul, I am trying to mock concrete classes but I got stuck cause my project is maven based and to setup a process to use the compiler plugin will be tricky. Anyway, cglib provides class mocking capabilities (as well as interfaces) and can replace the call to JavaProxy. Here is a java example from their web site:

    http://cglib.sourceforge.net/xref/samples/Beans.html

    That might work for scalamock

  4. 4 paul December 22, 2011 at 1:53 pm

    Hey Kostas,

    Maven support is on my list of things to do, but I know next-to-nothing about Maven, unfortunately, so it’s unlikely to happen soon I’m afraid. If you or anyone else fancies having a go, I’d be delighted to accept a contribution πŸ™‚

    Regarding cglib, I’m well aware of it. I deliberately chose not to use it because part of my motivation for ScalaMock was to support Android, and Android’s Dalvik VM doesn’t support cglib, unfortunately.

    I’m not opposed to a cglib backend for ScalaMock, and may look at it myself at some point, but it’s not something I’m likely to have time to investigate any time soon, I’m afraid. Again, I’d be very happy to accept contributions if anyone felt like giving it a go πŸ™‚

  5. 5 Me June 15, 2012 at 8:11 am

    Hey, penIsDown is a dangerous variable name to use.
    isPenDown is safer, even when erected.

  6. 7 AC July 15, 2012 at 8:16 pm

    When trying to run the test after cloning from GitHub, I get the following:
    [warn] ::::::::::::::::::::::::::::::::::::::::::::::
    [warn] :: UNRESOLVED DEPENDENCIES ::
    [warn] ::::::::::::::::::::::::::::::::::::::::::::::
    [warn] :: org.scalamock#scalamock-scalatest-support_2.9.1;2.3-SNAPSHOT: not found
    [warn] :: org.scalamock#scalamock-compiler-plugin_2.9.1;2.3-SNAPSHOT: not found
    [warn] ::::::::::::::::::::::::::::::::::::::::::::::
    [error] {file:/home/ … /ScalaMockExample/}ScalaMockExample/*:update: sbt.ResolveException: unresolved dependency: org.scalamock#scalamock-scalatest-support_2.9.1;2.3-SNAPSHOT: not found
    [error] unresolved dependency: org.scalamock#scalamock-compiler-plugin_2.9.1;2.3-SNAPSHOT: not found
    [error] Total time: 1 s, completed Jul 15, 2012 9:10:32 PM

    What could be wrong?

    • 8 paul July 16, 2012 at 12:17 am

      Sorry Alex – this looks to have rotted thanks to the fact that scala-tools has been retired. I’ve checked in changes that should get things working again – let me know if you continue to have problems after you pull.

  7. 9 her August 1, 2012 at 7:13 pm

    Hi Paul,

    I just cloned into git and had some problems when running “sbt test”.

    The error are as follows:

    Detected sbt version 0.12.0-Beta2
    [info] Loading project definition from […]/ScalaMockExample/project/project
    [info] Loading project definition from […]/ScalaMockExample/project
    [info] Set current project to ScalaMockExample (in build file:[…]/ScalaMockExample/)
    [info] Compiling 4 Scala sources to […]/ScalaMockExample/target/scala-2.9.2/test-classes…
    [error] […]/ScalaMockExample/src/test/scala/com/example/ColouredTurtleTest.scala:5: object generated is not a member of package org.scalamock
    [error] import org.scalamock.generated.GeneratedMockFactory
    [error] ^
    [error] […]/ScalaMockExample/src/test/scala/com/example/ColouredTurtleTest.scala:10: not found: value mock
    [error] val m1 = mock[ColouredTurtle]
    [error] ^
    [error] […]/ScalaMockExample/src/test/scala/com/example/ColouredTurtleTest.scala:11: not found: value mock
    [error] val m2 = mock[ColouredTurtle]
    [error] ^
    [error] […]/ScalaMockExample/src/test/scala/com/example/ColouredTurtleTest.scala:23: not found: value mockObject
    [error] val m = mockObject(InkReservoir)
    [error] ^
    [error] […]/ScalaMockExample/src/test/scala/com/example/ControllerTest.scala:5: object generated is not a member of package org.scalamock
    [error] import org.scalamock.generated.GeneratedMockFactory
    [error] ^
    [error] […]/ScalaMockExample/src/test/scala/com/example/ControllerTest.scala:11: not found: value mock
    [error] val mockTurtle = mock[Turtle]
    [error] ^
    [error] 6 errors found
    [error] {file:[…]/ScalaMockExample/}ScalaMockExample/test:compile: Compilation failed
    [error] Total time: 2 s, completed Aug 1, 2012 8:07:49 PM

    It might be something obvious, but I haven’t checked yet. You might want to amend this version or reply if you know a quick fix.

    Bye 4 now,
    her

  8. 10 her August 2, 2012 at 12:45 am

    Sorry, for my earlier comment.

    Never forget: “First read, than try!” ( sbt generate-mocks )

    Bye

  9. 11 her August 13, 2012 at 1:19 pm

    Hello Paul,

    me again, this time I have tested, played around and tried to find information on this issue beforehand but couldn’t find any help.

    Everything is working find, except that I can’t use MockFactory and GeneratedMocks with FunSpec, my prefered scalatest style. If I try to use FunSpec I get:

    [error] […]/testProject/src/test/scala/TestClass_tests_FunSpec.scala:6: FunSpec is not a member of org.scalatest
    [error] import org.scalatest.{ FunSpec }
    [error] ^
    [error] […]/testProject/src/test/scala/TestClass_tests_FunSpec.scala:10: not found: type FunSpec
    [error] class TestClass_tests_FunSpec extends FunSpec with MockFactory with GeneratedMockFactor
    [error] ^
    [error] […]/testProject/src/test/scala/TestClass_tests_FunSpec.scala:10: not found: type GeneratedMockFactor
    [error] class TestClass_tests_FunSpec extends FunSpec with MockFactory with GeneratedMockFactor
    [error] ^
    [error] three errors found
    [error] (test:compile) Compilation failed
    [error] Total time: 2 s, completed Aug 13, 2012 2:07:29 PM

    With FunSuite everything runs fine. The only thing I noticed was that I needed to store the libraryDependencies statement for scalatest in project/plugins.sbt what is a bit weired. If I store it under [ProjectHome]build.sbt it doesn’t work.

    Can you help me with the FunSpec issue or refer me to a source of information?

    I use:
    sbt 12.0.0
    scalatest 2.0.M3
    scala 2.9.2
    scalamock 2.4


  1. 1 Testing in Scala Trackback on December 5, 2014 at 12:27 pm

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





%d bloggers like this: