
- 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
Scala - Mocking Techniques
Mocking is an important technique in software testing. You can simulate and control the behavior of dependencies and external systems. It is used in testing code that interacts with external resources, like databases, web services, and third-party libraries. You can isolate the code under test and focus on its core logic without affecting the availability and state of external dependencies.
Mockito
Mockito is the most used mocking framework in the JVM ecosystem. It is simple and flexible. You can create mock objects, with behavior, and verifying interactions. But it also has some challenges, like, higher-order functions, traits, and type inference. We also have a mockito-scala library to resolve the above issues.
Getting Started with Mockito in Scala
You need to add the required dependencies to your build tool to get started with Mockito in Scala. You need to add the following to your build.sbt for SBT -
libraryDependencies ++= Seq( "org.scalatestplus" %% "mockito-5-10" % "3.2.18.0" % Test, "org.scalatest" %% "scalatest" % "3.2.18" % Test )
If you are using Maven, then you need to add this dependency -
<dependency> <groupId>org.scalatestplus</groupId> <artifactId>mockito-5-12_3</artifactId> <version>3.2.19.0</version> <scope>test</scope> </dependency>
Note that your project should use JDK 11 and higher because Mockito 5 requires it. If you are on JDK 8, then you will need to use mockito-4-11.
Creating and Using Mock Objects
Basic Mocking
You can create mock objects with ease using Mockito. You can create mocks and define their behavior using MockitoSugar -
val dao = mock[InventoryTransactionDao]
Mocking Methods
You can use the when().thenReturn() construct to define the behavior of a mock object method. For example -
when(dao.getAll()).thenReturn(Future.successful(Seq.empty[InventoryTransaction]))
You can also handle exceptions by using thenThrow -
when(dao.getAll()).thenThrow(new RuntimeException("Database error"))
Mocking Methods with Parameters
You can specify the behavior based on the input arguments for methods with parameters -
val txn = InventoryTransaction(1, "item1") when(dao.saveAsync(txn)).thenReturn(Future.successful(txn))
If the method takes any parameter of a certain type, you need to use any -
when(dao.saveAsync(any[InventoryTransaction])).thenReturn(Future.successful(txn))
Verifying Method Calls
You can verify interactions with your mock objects using the verify() method -
verify(mockProducer, times(1)).publish(any[InventoryTransaction])
For more precise verification, like checking that a method was never called -
verify(mockProducer, times(0)).publish(any[InventoryTransaction])
Or using the never helper method -
verify(mockProducer, never).publish(any[InventoryTransaction])
Argument Captors
Mockito has ArgumentCaptor to capture arguments passed to mock methods -
val refCapture = ArgumentCaptor.forClass(classOf[String]) verify(mockLogger).logTime(refCapture.capture(), any[LocalDateTime]) refCapture.getValue shouldBe "expectedValue"
Spies
Sometimes you need to use real methods for part of a mock object while stubbing others. You can achieve it using spies -
val dao = spy(new InventoryTransactionDaoImpl) when(dao.saveAsync(txn)).thenReturn(Future.successful(txn))
Partial Mocks
You need only specific methods are mocked while the rest behave normally for partial mocks -
val service = mock[OtherService] when(service.login("Anne", "xx")) thenReturn Some(User(222, "AA"))
Mockito-Scala Enhancements
Idiomatic Syntax
Mockito-Scala provides an idiomatic syntax for API more Scala-like. For example -
aMock.bar shouldReturn "mocked!" aMock.baz(*) shouldReturn "mocked!"
Improved Matchers
Mockito-Scala has enhanced matchers that are more consistent and readable -
aMock.method(n > 4.99) was called aMock.method(n <= 5) was called
Default Answers
Mockito-Scala has better defaults for handling non-stubbed method calls, using smart nulls and empty values -
implicit val defaultAnswer: DefaultAnswer = ReturnsEmptyValues orElse ReturnsSmartNulls
Cats and Scalaz Integration
You can integrate mockito-scala to work with these libraries for projects using Cats and Scalaz -
val aMock = mock[Foo] whenF(aMock.returnsOption(*)) thenReturn "mocked!"
Testing Strategies
Using Traits for Setup
You can use traits for common setup. It reduces boilerplate and improves test readability.
trait Setup { val myMock = mock[SomeService] } class MySpec extends WordSpec with Setup { "A test" should { "do something" in { myMock.someMethod returns "result" // test logic } } }
Example
You should have these dependencies in your build.sbt file -
import Dependencies._ ThisBuild / scalaVersion := "2.13.14" lazy val root = (project in file(".")) .settings( name := "Demo", version := "0.1", libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "3.2.18" % Test, "org.scalatestplus" %% "mockito-3-4" % "3.2.10.0" % Test ) )
Now, you can define MySpec.scala under src/test/scala folder -
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.mockito.MockitoSugar import org.mockito.Mockito._ trait SomeService { def someMethod: String } trait Setup extends MockitoSugar { val myMock = mock[SomeService] } class MySpec extends AnyWordSpec with Matchers with Setup { "A test" should { "do something" in { // Define the behavior of the mock when(myMock.someMethod).thenReturn("result") // Call the method and assert the result myMock.someMethod shouldEqual "result" // Additional test logic can be added here } } }
Note that your project SomeService is defined somewhere in your project. It can be a simple trait (or class) as shown in the test example.
Commands
Now, you can use these commands to compile and run test -
sbt compile sbt test
Output
[info] MySpec: [info] A test [info] - should do something [info] Run completed in 774 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: 5 s, completed 07-Aug-2024, 5:42:31Îûpm
Handling Mocked Objects in Sessions
Mockito-Scala has MockitoScalaSession to manage mock sessions and to verify proper behavior -
MockitoScalaSession().run { val foo = mock[Foo] when(foo.bar("test")) thenReturn "mocked" foo.bar("test") shouldBe "mocked" }
Best Practices
Mocking Traits and Classes
You can use the appropriate strategy based on your test requirements when mocking traits and classes. If you need to mock a method in a trait and use it in a class extending that trait -
val service = mock[OtherService] when(service.login("Anne", "xx")) thenReturn Some(User(222, "AA")) service.getId should be(222)
Partial Mocks and Composition
You can use composition over inheritance. You can inject dependencies through the constructor to facilitate mocking -
class OtherService(loginService: LoginService) { def getId: Int = { loginService.login("Anne", "xx").get.id } } val fakeService = mock[LoginService] when(fakeService.login("Anne", "xx")) thenReturn Some(User(222, "AA")) val other = new OtherService(fakeService) other.getId should be(222)
Using the IdiomaticMockito Trait
You can use the IdiomaticMockito trait -
class MyTest extends WordSpec with Matchers with IdiomaticMockito { "A service" should { "return a mocked value" in { val service = mock[Service] service.method shouldReturn "mocked!" service.method shouldBe "mocked!" } } }
Advanced Argument Matching
You can use argThat and custom matchers for argument matching -
when(service.method(argThat(new ArgumentMatcher[CustomType] { def matches(argument: CustomType): Boolean = argument.field == "expected" }))).thenReturn("mocked!")
Or using Scala syntax -
when(service.method(argMatching { case CustomType("expected") => true case _ => false })).thenReturn("mocked!")
Handling By-Name Arguments
Mockito-Scala has full support for by-name arguments. So you can stub methods with such parameters -
class Foo { def byNameMethod(x: => String): String = x } val foo = mock[Foo] when(foo.byNameMethod(any[String])) thenReturn "mocked!" foo.byNameMethod("test") shouldBe "mocked!"
Mocking Scala Objects
Mockito-scala is used to mock Scala object methods -
object FooObject { def method: String = "not mocked!" } withObjectMocked[FooObject.type] { FooObject.method returns "mocked!" FooObject.method shouldBe "mocked!" } FooObject.method shouldBe "not mocked!"