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 objectsYou 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 ScalaMockScalaMock 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 import org.scalatest.flatspec.AnyFlatSpec import org.scalamock.scalatest.MockFactory class ExampleSpec extends AnyFlatSpec with MockFactory with ... Function mocksFunction mocks are created with 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 m expects (42) returning "Forty two" once Proxy mocksProxy 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 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 m expects 'forward anyNumberOfTimes m stubs 'forward Generated mocksGenerated 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 val m = mock[Turtle] m.expects.forward(10.0) twice To mock a singleton or companion object, use val m = mockObject(Turtle) m.expects.createTurtle And to mock a constructor invocation, use val m = mock[Turtle] m.expects.newInstance('blue) m.expects.forward(10.0) ExpectationsYou 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. ArgumentsTo 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: m expects ("this", "that") ScalaMock currently supports two types of generalized matching: wildcards and epsilon matching. WildcardsWildcard values are specified with an
m expects ("this", *)
will match any of the following: m("this", 42) m("this", 1.0) m("this", null) Epsilon matchingEpsilon matching is useful when dealing with floating point values. An epsilon match is
specified with the
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 parametersIf 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 def takesRepeatedParameter(x: Int, ys: String*) you can set an expectation with:
m expects 'takesRepeatedParameter withArgs(42, **("red", "green", "blue"))
Predicate matchingMore complicated argument matching can be implemented by using m = mockFunction[Double, Double, Unit] m expects { where _ < _ } m = mock[Turtle] m expects 'setPosition where { (x: Double, y: Double) => x < y } Return valueYou can instruct a mock to return a specific value with m1 returns 42 m2 expects ("this", "that") returning "the other" If no return value is specified, functional mocks return If no return value is specified, proxy mocks return You can return a computed value (or throw a computed exception) with val mockIncrement = mockFunction[Int, Int] m expects (*) onCall { x: Int => x + 1 } ExceptionsInstead of a return value, a mock can be instructed to throw: m expects ("this", "that") throws new RuntimeException("what's that?") Call countBy 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 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 OrderingBy 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 { 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 m.expects.a inSequence { m.expects.b inAnyOrder { m.expects.c inSequence { m.expects.d m.expects.e } m.expects.f } m.expects.g } DebuggingIf faced with a difficult to debug failing expectation, consider mixing
one or both of the 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.