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 @@

Testing with mock objects

Using ScalaMock

-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 @@

Using ScalaMock

class ExampleSpec extends FlatSpec with MockFactory with ... -

Function mocks

+

Mocking functions

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

@@ -89,7 +68,7 @@

Function mocks

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 +

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":

@@ -97,302 +76,137 @@

Function mocks

m expects (42) returning "Forty two" once -

Proxy mocks

+

Mocking objects

-

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

- -

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", *)
+(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

- -

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"))
-
+

Two mocking styles

+

+ScalaMock supports two different mocking styles—expectations-first and record-then-verify. These styles can be mixed within a single test.

+

-

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:

+To use this style use 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) -

Return value

+ (heaterStub.isReady _).when().returns(true) -

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

+ coffeeMachine.makeCoffee() -
-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.)

+

Argument Matching

-

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:

-

Exceptions

+
    +
  • wildcards
  • +
  • epsilon matching
  • +
  • predicate 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 using returning() method (or returns() 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 to inSequence and inAnyOrder 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 or org.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) }