diff --git a/app/views/userGuide/testingWithMockObjects.scala.html b/app/views/userGuide/testingWithMockObjects.scala.html index 5478601d7d..2df77575bb 100644 --- a/app/views/userGuide/testingWithMockObjects.scala.html +++ b/app/views/userGuide/testingWithMockObjects.scala.html @@ -45,31 +45,10 @@
-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 org.scalamock.scalatest.MockFactory
into your Suite
class,
as in:
@@ -80,7 +59,7 @@
Function mocks are created with mockFunction
. The following, for example, creates a mock
function taking a single Int
argument and returning a String
:
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 42
, and that when called like that
it should return the string "Forty two"
:
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
+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]
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 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) -- -
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 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.
- -Wildcard values are specified with an *
(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)-
will match any of the following:
+By default, an expectation accepts a single call. The following two statements are equivalent:
-m("this", 42) -m("this", 1.0) -m("this", null) +(turtleMock.forward_) expects (*) once +(turtleMock.forward_) expects (*)-
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) -- -
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"))
-
++ScalaMock supports two different mocking styles—expectations-first and record-then-verify. These styles can be mixed within a single test.
+-
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) }