From eb717d1f44cfc4e17f4e1289c88d938e13a1214c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Wiejacha?=
-ScalaMock is a native, open-source Scala mocking
+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:
To use ScalaMock, mix Function mocks are created with You can then set expectations set on the mock function. For example, here's how you'd state that you
+ You can then set expectations on the mock function. For example, here's how you'd state that you
expect your mock to be called once with the argument 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 You can then set expectations on each of the methods within those traits. Here's an example: By default, an expectation accepts any arguments and a single call. The following two statements are equivalent: As a convenience, proxy mocks also support the Generated mocks rely on the ScalaMock compiler plugin.
-Classes that are going to be mocked need to be declared with the In addition to Then, to create a regular mock object, use To mock a singleton or companion object, use And to mock a constructor invocation, use 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. To specify expected arguments for a functional mock, use 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: ScalaMock currently supports two types of generalized matching: wildcards and epsilon
-matching. Wildcard values are specified with an will match any of the following: By default, an expectation accepts a single call. The following two statements are equivalent: Epsilon matching is useful when dealing with floating point values. An epsilon match is
-specified with the will match: but will not match: 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 you can set an expectation with:
+ScalaMock supports two different mocking styles—expectations-first and record-then-verify. These styles can be mixed within a single test.
-Testing with mock objects
Using ScalaMock
-
-
org.scalamock.scalatest.MockFactory
into your Suite
class,
as in:
@@ -80,7 +59,7 @@ Using ScalaMock
class ExampleSpec extends FlatSpec with MockFactory with ...
-Function mocks
+Mocking functions
mockFunction
. The following, for example, creates a mock
function taking a single Int
argument and returning a String
:Function mocks
val m = mockFunction[Int, String]
-42
, and that when called like that
it should return the string "Forty two"
:Function mocks
m expects (42) returning "Forty two" once
-Proxy mocks
+Mocking objects
-org.scalamock.ProxyMockFactory
into your test suite.
-Proxy mocks are created with mock
. The following, for example, creates a mock that implements
+You can also mock Scala and Java classes, traits and interfaces. Object mocks are created with mock
. The following, for example, creates a mock that implements
all the Turtle
trait (interface):
-val m = mock[Turtle]
+val turtleMock = mock[Turtle]
-m expects 'setPosition withArgs (10.0, 10.0)
-m expects 'forward withArgs (5.0)
-m expects 'getPosition returning (15.0, 10.0)
-
-
-
-m expects 'forward withArgs (*) once
-m expects 'forward
-
-
-stubs
method. The following two statements are equivalent:
-m expects 'forward anyNumberOfTimes
-m stubs 'forward
-
-
-
-Generated mocks
-
-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
.MockFactory
, your test class also needs to mix in GeneratedMockFactory
.mock
:
-val m = mock[Turtle]
-
-m.expects.forward(10.0) twice
-
-
-mockObject
:
-val m = mockObject(Turtle)
-
-m.expects.createTurtle
-
-
-newInstance
:
-val m = mock[Turtle]
-
-m.expects.newInstance('blue)
-m.expects.forward(10.0)
-
-
-Expectations
-
-Arguments
-
-expects
. To specify expected
-arguments for a proxy mock, use withArgs
or withArguments
.
-m expects ("this", "that")
-
-
-Wildcards
-
-*
(asterisk). For example:
-m expects ("this", *)
+(turtleMock.setPosition _) expects (10.0, 10.0)
+(turtleMock.forward _) expects (5.0)
+(turtleMock.getPosition _) expects() returning (15.0, 10.0)
-
-m("this", 42)
-m("this", 1.0)
-m("this", null)
+(turtleMock.forward_) expects (*) once
+(turtleMock.forward_) expects (*)
-Epsilon matching
-
-~
(tilde) operator:
-m expects (~42.0)
-
-
-
-m(42.0)
-m(42.0001)
-m(41.9999)
-
-
-
-m(43.0)
-m(42.1)
-
-
-Repeated parameters
-
-**
operator. For example, given:
-def takesRepeatedParameter(x: Int, ys: String*)
-
-
-
-m expects 'takesRepeatedParameter withArgs(42, **("red", "green", "blue"))
-
+Two mocking styles
+Predicate matching
+In the expectations-first style (which was used in previous examples), expectations are set on mock objects before exercising the system under test. If these expectations are not met, the test fails.
+
You can also use record-then-verify (Mockito) mocking style, where expectations are verified after the system under test has executed.
-More complicated argument matching can be implemented by using where
to pass a predicate:
stub
instead of mock
:
-m = mockFunction[Double, Double, Unit] -m expects { where _ < _ } +val heaterStub = stub[Heater]- +
Return values that are used by the system under test can be set up by using when
before running the tested system. Calls are verified using verify
:
-m = mock[Turtle] -m expects 'setPosition where { (x: Double, y: Double) => x < y } -+"CoffeeMachine" should "turn off the heater after coffee making is finished" in { + val heaterStub = stub[Heater] + val coffeeMachine = new CoffeeMachine(heaterStub) -
You can instruct a mock to return a specific value with returns
or returning
:
-m1 returns 42 -m2 expects ("this", "that") returning "the other" + (heaterStub.setPowerState _).verify(PowerState.On) + (heaterStub.setPowerState _).verify(PowerState.Off) +}-
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.).
You can read more about ScalaMock mocking styles in its User Guide.
-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 } -+
ScalaMock supports three types of generalised matching:
-Instead of a return value, a mock can be instructed to throw:
+// expect someMethod("foo", 42) to be called +(myMock.someMethod _).expects("foo", 42) --m expects ("this", "that") throws new RuntimeException("what's that?") -+// expect someMethod("foo", x) to be called for some integer x +(myMock.someMethod _).expects("foo", *) -Call count
+// expect someMethod("foo", x) to be called for some float x that is close to 42.0 +(myMock.otherMethod _).expects("foo", ~42.0) -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 +// expect sendMessage(receiver, message) for some receiver with name starting with "A" +(myMock.sendMessage _).expects(where { (receiver: Player, message: Message) => + receiver.name.startsWith("A") +})-There are various aliases for common expectations and styles:
+Ordering
+By default, expectations can be satisfied in any order. A specific sequence can be enforced with
+inSequence
// expect that machine is turned on before turning it off +inSequence { + (machineMock.turnOn _).expects() + (machineMock.turnOff _).expects() +} --m1 expects ("this", "that") once -m2 returns "foo" noMoreThanTwice -m3 expects (42) repeated 3 times +// players can be fetched in any order +inAnyOrder { + (databaseMock.getPlayerByName _).expects("Hans") + (databaseMock.getPlayerByName _).expects("Boris") +}-For a full list, see
+org.scalamock.Expectation
.Call counts
+By default, mocks and stubs expect exactly one call. But you can also specify alternative constraints, for example:
-Ordering
+// expect message to be sent twice +(myMock.sendMessage _).expects(*).twice -By default, expectations can be satisfied in any order. For example:
+// expect message to be sent any number of times +(myMock.sendMessage _).expects(*).anyNumberOfTimes --m expects (1) -m expects (2) -m(2) -m(1) +// expect message to be sent no more than twice +(myMock.sendMessage _).expects(*).noMoreThanTwice-A specific sequence can be enforced with
+inSequence
:Returning values
--inSequence { - m expects (1) - m expects (2) -} -m(2) // throws ExpectationException -m(1) +By default mocks and stubs return
+null
. You can return predefined value usingreturning()
method (orreturns()
in case of stubs).(databaseMock.getPlayerByName _).expects("Hans").returning(Player(name="Hans", country="Germany")) +(databaseMock.getPlayerByName _).expects("Boris").returning(Player(name="Hans", country="Russia"))-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:
+Throwing exceptions
--val m1 = mock[Turtle] -val m2 = mock[Turtle] +Instead of a return value, mocks and stubs can be instructed to throw. This can be achieved by using+throwing
method. -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) -} +(databaseMock.getPlayerByName _).expects("George").throwing(new NoSuchElementException)-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 } -Call handlers
-To specify that there is no constraint on ordering, use
+inAnyOrder
(there is an implicit -inAnyOrder
at the top level). Calls toinSequence
andinAnyOrder
can be arbitrarily -nested. For example:When returned value depends on function arguments, you can return the computed value (or throw a computed exception) with
-onCall()
.-m.expects.a -inSequence { - m.expects.b - inAnyOrder { - m.expects.c - inSequence { - m.expects.d - m.expects.e - } - m.expects.f - } - m.expects.g -} -+// compute returned value +(fooMock.increment _) expects(*) onCall { arg: Int => arg + 1} +fooMock.increment(100) shouldBe 101 -Debugging
- -If faced with a difficult to debug failing expectation, consider mixing -one or both of the
- -org.scalamock.VerboseErrors
ororg.scalamock.CallLogging
traits -into your test suite:-class ExampleSpec extends FlatSpec with MockFactory with - VerboseErrors with CallLogging ... +// throw computed exception +(fooMock.increment _) expects(*) onCall { arg: Int => throw new RuntimeException(arg) }