This page of ScalaTest.org!is brought to you by: |
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 Using Selenium Other goodies Philosophy and design Migrating to 2.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 described on this page: 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 import org.scalamock.scalatest.MockFactory class ExampleSpec extends FlatSpec 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 FlatSpec with MockFactory with VerboseErrors with CallLogging ... Using EasyMock
ScalaTest's Using the EasyMock API directly, you create a mock with: val mockCollaborator = createMock(classOf[Collaborator]) With this trait, you can shorten that to: val mockCollaborator = mock[Collaborator] After creating mocks, you set expectations on them, using syntax like this: mockCollaborator.documentAdded("Document") mockCollaborator.documentChanged("Document") expectLastCall().times(3)
If you wish to highlight which statements are setting expectations on the mock (versus
which ones are actually using the mock), you can place them in an expecting { mockCollaborator.documentAdded("Document") mockCollaborator.documentChanged("Document") lastCall.times(3) }
Using an
Once you've set expectations on the mock objects, you must invoke replay(mockCollaborator) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) verify(mockCollaborator) This trait enables you to use the following, more declarative syntax instead: whenExecuting(mockCollaborator) { classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) }
The
To summarize, here's what a typical test using val mockCollaborator = mock[Collaborator]
An alternative approach is to place your mock objects in a implicit val mocks = MockObjects(mock[Collaborator]) Using JMock
ScalaTest's
Using the JMock API directly, you first need a val context = new Mockery
context.setImposteriser(ClassImposteriser.INSTANCE)
When using this class, you would instead create an instance of this class (which will create and
wrap a val cycle = new JMockCycle import cycle._ Using the JMock API directly, you would create a mock object like this: val mockCollaborator = context.mock(classOf[Collaborator]) Having imported the members of an instance of this class, you can shorten that to: val mockCollaborator = mock[Collaborator] After creating mocks, you set expectations on them, using syntax like this: context.checking( new Expectations() { oneOf (mockCollaborator).documentAdded("Document") exactly(3).of (mockCollaborator).documentChanged("Document") } ) Having imported the members of an instance of this class, you can shorten this step to: expecting { e => import e._ oneOf (mockCollaborator).documentAdded("Document") exactly(3).of (mockCollaborator).documentChanged("Document") }
The
The oneOf (mockCollaborator).documentAdded(`with`("Document"))
By importing the members of the passed
oneOf (mockCollaborator).documentAdded(withArg("Document"))
Once you've set expectations on the mock objects, when using the JMock API directly, you use the mock, then invoke
classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) context.assertIsSatisfied() This class enables you to use the following, more declarative syntax instead: whenExecuting { classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) }
The
To summarize, here's what a typical test using val cycle = new JMockCycle import cycle._
ScalaTest also provides a Using Mockito
ScalaTest's Using the Mockito API directly, you create a mock with: val mockCollaborator = mock(classOf[Collaborator]) Using this trait, you can shorten that to: val mockCollaborator = mock[Collaborator]
Here is how the example used in the previous EasyMock and JMock sections would look with Mockito and // First, create the mock object val mockCollaborator = mock[Collaborator] // Create the class under test and pass the mock to it classUnderTest = new ClassUnderTest classUnderTest.addListener(mock) // Use the class under test classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) classUnderTest.addDocument("Document", new Array[Byte](0)) // Then verify the class under test used the mock object as expected verify(mockCollaborator).documentAdded("Document") verify(mockCollaborator, times(3)).documentChanged("Document") Next, learn about property-based testing. |
ScalaTest is brought to you by Bill Venners, with
contributions from several other folks. It is sponsored by
Artima, Inc.
ScalaTest is free, open-source software
released under the Apache
2.0 license.
Copyright © 2009-2013 Artima, Inc. All Rights Reserved.