
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Methods
- Scala - Functions
- Scala - Methods vs Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala - Polymorphic Functions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - NumericRange
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala Advanced Types
- Scala - Union Types
- Scala - Intersection Types
- Scala - Type Aliases
- Scala - Structural Types
- Scala - Match Expression
- Scala - Singleton Type Operator
- Scala - Abstract Types
- Scala - Dependent Types
- Scala - Abstract Type Bounds
- Scala - Higher-Kinded Types
- Scala - Opaque Type Alias
- Scala - Path-Dependent Types
- Scala - Type Lambdas
- Scala - Type Inference
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Guards
- Scala - Variables in Patterns
- Scala - Type Patterns
- Scala - The Matchable Trait
- Scala - Matching Arrays
- Scala - Matching Lists
- Scala - Matching Tuples
- Scala - Exception Handling
- Scala - Extractors
- Scala - Pattern Bindings
- Scala - Regular Expressions
- Scala - Case Classes
- Scala - Partial Functions
- Scala - Packaging and Imports
- Scala - Implicit Imports
- Scala - Export Clauses
- Scala - Nested Packages
- Scala - Chained Packages
- Scala - Package Objects
- Scala Files I/O
- Scala - Files I/O
- Scala - Writing Files
- Scala - Listing Files
- Scala - Deleting Directories
- Scala - Check File Exists
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Process Management
- Scala - Scaladoc
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala Unit Testing
- Scala - Unit Testing
- Scala - uTest
- Scala - MUnit
- Scala - ScalaTest Runner
- Scala - ScalaMock
- Scala - JUnit
- Scala - Mocking
- Scala - BDD Testing
ScalaMock for Effective Unit Testing
ScalaMock is a mocking framework for Scala applications. You can simulate external dependencies and isolate the unit under test. You can create mock objects, set expectations on their interactions. You can also verify that your code behaves as expected when interacting with these simulated dependencies.
You mock dependencies that involve network communication and file I/O. You can replace these real dependencies with mocks that simulate their behavior for faster tests, more reliable, and easier to write.
Setup
You need to add the following dependency to your build.sbt file to start using ScalaMock -
libraryDependencies += "org.scalamock" %% "scalamock" % "6.0.0" % Test
Now, you can import the required classes in your test suite file -
import org.scalamock.scalatest.MockFactory import org.scalatest.flatspec.AnyFlatSpec
Argument Matching
ScalaMock provides various options for argument matching, like, exact, wildcard, and predicate matching.
Exact Match -
(mockObject.method _).expects(arg1, arg2) Wildcard Match: (mockObject.method _).expects(*)
Predicate Match -
(mockObject.method _).expects(where { arg => arg > 5 })
Epsilon Matching
ScalaMock supports epsilon matching to account for precision issues for floating-point comparisons -
(mockObject.method _).expects(~42.0)
Ordered Verification
You can verify the execution order of mocked methods for correct sequence of operations.
In Sequence -
inSequence { (mockObject.method1 _).expects() (mockObject.method2 _).expects() }
In Any Order -
inAnyOrder { (mockObject.method1 _).expects() (mockObject.method2 _).expects() }
Call Count Verification
You can check the number of calls to a mocked method to match expectations.
Exact Number of Calls -
(mockObject.method _).expects().twice()
Range of Calls -
(mockObject.method _).expects().repeat(3 to 5)
Returning Values and Exception Throwing
You can control the return values and exceptions of mocked methods for more precise testing.
Returning Values -
(mockObject.method _).expects().returning(value)
Throwing Exceptions -
(mockObject.method _).expects().throwing(new Exception("Error"))
Call Handlers
You can compute the return value based on the given arguments using call handlers -
(mockObject.method _).expects(*).onCall((arg: Int) => arg + 1)
Argument Capture
You can capture arguments passed to mocked methods for later verification. It is used for more detailed assertions -
val captor = CaptureOne[Int]() (mockObject.method _).expects(capture(captor))
Mocking Styles
There are two primary mocking styles in ScalaMock: expectations-first and record-then-verify.
Expectations-First -
val mockObject = mock[Service] (mockObject.method _).expects(arg).returning(result)
Record-Then-Verify -
val mockObject = stub[Service] (mockObject.method _).when(arg).returns(result)
Example
Consider this example to mock a Formatter trait in a Greetings object to test interactions using ScalaMock. We set up a mock for the Formatter trait and verified that the sayHello method in the Greetings object behaves as expected.
You must have this build.sbt file with following dependencies -
import Dependencies._ ThisBuild / scalaVersion := "2.13.14" lazy val root = (project in file(".")) .settings( name := "Demo", version := "0.1", libraryDependencies ++= Seq( "org.scalamock" %% "scalamock" % "6.0.0" % Test, "org.scalatest" %% "scalatest" % "3.2.10" % Test ) )
Now, you need to define Greetings.scala under src/main/scala with this code -
trait Formatter { def format(s: String): String } object Greetings { def sayHello(name: String, formatter: Formatter): Unit = { println(formatter.format(name)) } }
Now, you need to define GreetingsTest.scala under src/test/scala with this code -
import org.scalamock.scalatest.MockFactory import org.scalatest.flatspec.AnyFlatSpec class GreetingsTest extends AnyFlatSpec with MockFactory { "sayHello" should "format the name correctly" in { val mockFormatter = mock[Formatter] (mockFormatter.format _).expects("Mr Bond").returning("Ah, Mr Bond. I've been expecting you").once() Greetings.sayHello("Mr Bond", mockFormatter) } }
Commands
You can clean and compile using this command -
sbt clean compile
Now you execute the tests using the following command -
sbt test
Output
Ah, Mr Bond. I've been expecting you [info] GreetingsTest: [info] sayHello [info] - should format the name correctly [info] Run completed in 271 milliseconds. [info] Total number of tests run: 1 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 4 s, completed 07-Aug-2024, 12:04:25Îûpm
Mocking Higher-Order Functions
You can challenge Mocking higher-order functions due to function reference equality. You can use predicate matching to overcome this limitation.
(mockObject.method(_: () => String)).expects(where { f => f() == "expected" }).returns("result")
Integration with Other Testing Frameworks
There are various testing frameworks to integrate with ScalaMock, like ScalaTest and Specs2. You can use AsyncMockFactory for ScalaTest async specs.
Mocking in Scala 3
ScalaMock supports Scala 3 with some migration notes -
- Use type annotations for methods with by-name parameters.
- Mocking of non-abstract Java classes requires workarounds.
Mocking Functions
Simple Functions
You can mock simple functions similarly to classes and traits using mockFunction -
val mockF = mockFunction[Int, Int] mockF.expects(*).onCall((i: Int) => i * 2).anyNumberOfTimes() assert(mockF.apply(1) === 2) assert(mockF.apply(11) === 22)
Polymorphic Functions
You can mock polymorphic functions by specifying the type in the expectation declaration -
trait Polymorphic { def call[A](arg: A): A } val mockPolymorphic = mock[Polymorphic] (mockPolymorphic.call[Int] _).expects(1).onCall((i: Int) => i * 2) assert(mockPolymorphic.call(1) === 2)
Overloaded Functions
You can mock overloaded functions by declaring the argument types explicitly -
trait Overloader { def f(i: Int): String def f(s: String): String def f(t: (Int, String)): String } val mockedOverloader = mock[Overloader] (mockedOverloader.f(_: Int)).expects(*).onCall((i: Int) => s"Int variant $i") (mockedOverloader.f(_: String)).expects(*).onCall((i: String) => s"String variant $i") (mockedOverloader.f(_: (Int, String))).expects(*).onCall((i: (Int, String)) => s"Tuple variant (${i._1}, ${i._2})") assert(mockedOverloader.f(1) === "Int variant 1") assert(mockedOverloader.f("str") === "String variant str") assert(mockedOverloader.f((1, "str")) === "Tuple variant (1, str)")
Curried Functions
You can mock curried functions by filling each argument list in the expectation call -
trait CurryFunc { def curried(i: Int)(str: String): List[String] } val mockedCurryFunc = mock[CurryFunc] (mockedCurryFunc.curried(_: Int)(_: String)).expects(*, *).onCall((i, str) => Range(0, i).map(num => s"$str-$num").toList) assert(mockedCurryFunc.curried(2)("myStr") === List("myStr-0", "myStr-1"))
Comparing ScalaMock with Mockito
Both ScalaMock and Mockito are popular mocking frameworks. But these have some differences as given below -
- Mocking vals, vars, and class members: Mockito can mock these. Whereas ScalaMock cannot.
- Syntax: ScalaMock may require less boilerplate and is more idiomatic for Scala.
Mockito
class Foo { def call(f: Int => String, i: Int): String = f(i) } val mockFoo = mock[Foo] when(mockFoo.call(any(), anyInt())).thenAnswer { invocation => val intArg = invocation.getArgument(1, classOf[Int]) (0 until intArg).mkString(",") } assert(mockFoo.call(_ => "bla", 3) == "0,1,2")
ScalaMock
trait Foo { def call(f: Int => String, i: Int): String } val mockFoo = mock[Foo] (mockFoo.call _).expects(*, *).onCall((f: Int => String, i: Int) => (0 until i).mkString(",")) assert(mockFoo.call(_ => "bla", 3) == "0,1,2")