ScalaTest User Guide

Getting started

Selecting testing styles

Defining base classes

Writing your first test

Using assertions

Tagging your tests

Running your tests

Sharing fixtures

Sharing tests

Using matchers

Testing with mock objects

Property-based testing

Asynchronous testing

Using Scala-js

Using Inside

Using OptionValues

Using EitherValues

Using PartialFunctionValues

Using PrivateMethodTester

Using WrapWith

Philosophy and design

Migrating to 3.0

Testing with mock objects

You can use any Java mocking framework with ScalaTest, or ScalaMock, a Scala mocking alternative. ScalaTest provides just enough syntax sugar for the three most popular Java mocking frameworks—JMock, EasyMock, and Mockito—to remove boilerplate and clarify the client code. But other than this minor syntax sugar, the libraries are used from Scala as they are in Java. This enables people familiar with a particular framework to get going quickly and enhances readability for combined Java/Scala projects using a common mocking framework. ScalaMock, the native Scala mocking framework, has been designed to integrate easily into ScalaTest suites.

Here are links to the mocking frameworks that can be used with ScalaTest:

Using ScalaMock

ScalaMock is a native, open-source Scala mocking framework written by Paul Butcher that allows you to mock objects and functions. ScalaMock supports three different mocking styles:

  • Function mocks
  • Proxy (dynamic) mocks
  • Generated (type-safe) mocks

To use ScalaMock, mix org.scalamock.scalatest.MockFactory into your Suite class, as in:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalamock.scalatest.MockFactory

class ExampleSpec extends AnyFlatSpec with MockFactory with ...

Function mocks

Function mocks are created with mockFunction. The following, for example, creates a mock function taking a single Int argument and returning a String:

val m = mockFunction[Int, String]

You can then set expectations set on the mock function. For example, here's how you'd state that you expect your mock to be called once with the argument 42, and that when called like that it should return the string "Forty two":

m expects (42) returning "Forty two" once

Proxy mocks

Proxy mocks can only be used to mock Scala traits and Java interfaces. (To mock classes, singleton/companion objects etc., please use generated mocks.) To use proxy mocks, mix org.scalamock.ProxyMockFactory into your test suite. Proxy mocks are created with mock. The following, for example, creates a mock that implements all the Turtle trait (interface):

val m = mock[Turtle]

You can then set expectations on each of the methods within those traits. Here's an example:

m expects 'setPosition withArgs (10.0, 10.0)
m expects 'forward withArgs (5.0)
m expects 'getPosition returning (15.0, 10.0)

By default, an expectation accepts any arguments and a single call. The following two statements are equivalent:

m expects 'forward withArgs (*) once
m expects 'forward

As a convenience, proxy mocks also support the stubs method. The following two statements are equivalent:

m expects 'forward anyNumberOfTimes
m stubs 'forward

Generated mocks

Generated mocks rely on the ScalaMock compiler plugin. Classes that are going to be mocked need to be declared with the org.scalamock.annotation.mock annotation. To mock a class together with its companion object, use org.scalamock.annotation.mockWithCompanion. To mock a standalone singleton object, use org.scalamock.annotation.mockObject.

In addition to MockFactory, your test class also needs to mix in GeneratedMockFactory.

Then, to create a regular mock object, use mock:

val m = mock[Turtle]

m.expects.forward(10.0) twice

To mock a singleton or companion object, use mockObject:

val m = mockObject(Turtle)

m.expects.createTurtle

And to mock a constructor invocation, use newInstance:

val m = mock[Turtle]

m.expects.newInstance('blue)
m.expects.forward(10.0)

Expectations

You can specify expectations about the arguments with which a function or method is called and how many times it will be called. In addition, you can instruct a mock to return a particular value or throw an exception when that expectation is met.

Arguments

To specify expected arguments for a functional mock, use expects. To specify expected arguments for a proxy mock, use withArgs or withArguments.

If no expected arguments are given, mocks accept any arguments.

To specify arguments that should simply be tested for equality, provide the expected arguments as a tuple:

m expects ("this", "that")

ScalaMock currently supports two types of generalized matching: wildcards and epsilon matching.

Wildcards

Wildcard values are specified with an * (asterisk). For example:

m expects ("this", *)

will match any of the following:

m("this", 42)
m("this", 1.0)
m("this", null)

Epsilon matching

Epsilon matching is useful when dealing with floating point values. An epsilon match is specified with the ~ (tilde) operator:

m expects (~42.0)

will match:

m(42.0)
m(42.0001)
m(41.9999)

but will not match:

m(43.0)
m(42.1)

Repeated parameters

If you're using generated mocks, you need do nothing special to set expectations on methods that take repeated parameters. If you're using proxy mocks you will need to use the ** operator. For example, given:

def takesRepeatedParameter(x: Int, ys: String*)

you can set an expectation with:

m expects 'takesRepeatedParameter withArgs(42, **("red", "green", "blue"))

Predicate matching

More complicated argument matching can be implemented by using where to pass a predicate:

m = mockFunction[Double, Double, Unit]
m expects { where _ < _ }
m = mock[Turtle]
m expects 'setPosition where { (x: Double, y: Double) => x < y }

Return value

You can instruct a mock to return a specific value with returns or returning:

m1 returns 42
m2 expects ("this", "that") returning "the other"

If no return value is specified, functional mocks return null.asInstanceOf[R], where R is the return type (which equates to 0 for Int, 0.0 for Double etc.).

If no return value is specified, proxy mocks return null. This works correctly for most return types, but not for methods returning primitive types (Int, Double etc.), where returning null leads to a NullPointerException. So you will need to explicitly specify a return value for such methods. (This restriction may be lifted in the future.)

You can return a computed value (or throw a computed exception) with onCall, for example:

val mockIncrement = mockFunction[Int, Int]
m expects (*) onCall { x: Int => x + 1 }

Exceptions

Instead of a return value, a mock can be instructed to throw:

m expects ("this", "that") throws new RuntimeException("what's that?")

Call count

By default, mocks expect one or more calls (i.e., only fail if the function or method is never called). An exact number of calls or a range can be set with repeat:

m1 returns 42 repeat 3 to 7
m2 expects (3) repeat 10

There are various aliases for common expectations and styles:

m1 expects ("this", "that") once
m2 returns "foo" noMoreThanTwice
m3 expects (42) repeated 3 times

For a full list, see org.scalamock.Expectation.

Ordering

By default, expectations can be satisfied in any order. For example:

m expects (1)
m expects (2)
m(2)
m(1)

A specific sequence can be enforced with inSequence:

inSequence {
  m expects (1)
  m expects (2)
}
m(2) // throws ExpectationException
m(1)

Multiple sequences can be specified. As long as the calls within each sequence happen in the correct order, calls within different sequences can be interleaved. For example:

val m1 = mock[Turtle]
val m2 = mock[Turtle]

inSequence {
  m1.expects.setPosition(0.0, 0.0)
  m1.expects.penDown
  m1.expects.forward(10.0)
  m1.expects.penUp
}
inSequence {
  m2.expects.setPosition(1.0, 1.0)
  m2.expects.turn(90.0)
  m2.expects.forward(1.0)
  m2.expects.getPosition returning (2.0, 1.0)
}

m2.setPosition(1.0, 1.0)
m1.setPosition( 0.0, 0.0)
m1.penDown
m2.turn(90.0)
m1.forward(10.0)
m2.forward(1.0)
m1.penUp
expect((2.0, 1.0)) { m2.getPosition }

To specify that there is no constraint on ordering, use inAnyOrder (there is an implicit inAnyOrder at the top level). Calls to inSequence and inAnyOrder can be arbitrarily nested. For example:

m.expects.a
inSequence {
  m.expects.b
  inAnyOrder {
    m.expects.c
    inSequence {
      m.expects.d
      m.expects.e
    }
    m.expects.f
  }
  m.expects.g
}

Debugging

If faced with a difficult to debug failing expectation, consider mixing one or both of the org.scalamock.VerboseErrors or org.scalamock.CallLogging traits into your test suite:

class ExampleSpec extends AnyFlatSpec with MockFactory with
    VerboseErrors with CallLogging ...

Next, learn about property-based testing.

ScalaTest is brought to you by Bill Venners and Artima.
ScalaTest is free, open-source software released under the Apache 2.0 license.

If your company loves ScalaTest, please consider sponsoring the project.

Copyright © 2009-2024 Artima, Inc. All Rights Reserved.

artima