scalaintermediate
Testing with Mocks and Stubs
Write testable Scala code with trait-based mocks, stubs, and dependency substitution.
scalaPress ⌘/Ctrl + Shift + C to copy
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
// Interfaces
trait Clock:
def now(): Long
trait Logger:
def info(msg: String): Unit
def error(msg: String): Unit
trait UserRepository:
def findById(id: Long): Option[String]
def save(name: String): Long
def delete(id: Long): Boolean
trait EmailSender:
def send(to: String, subject: String, body: String): Boolean
// Service under test
class UserService(
repo: UserRepository,
email: EmailSender,
logger: Logger,
clock: Clock
):
def register(name: String, emailAddr: String): Either[String, Long] =
if name.isEmpty then
logger.error("Empty name")
Left("Name is required")
else
val id = repo.save(name)
logger.info(s"Registered user $id at ${clock.now()}")
email.send(emailAddr, "Welcome", s"Hello $name")
Right(id)
def deregister(id: Long): Either[String, Unit] =
repo.findById(id) match
case None =>
Left(s"User $id not found")
case Some(name) =>
repo.delete(id)
logger.info(s"Deleted user $id")
Right(())
// Stubs
class StubClock(time: Long) extends Clock:
def now(): Long = time
class StubLogger extends Logger:
var infos: List[String] = Nil
var errors: List[String] = Nil
def info(msg: String): Unit = infos = infos :+ msg
def error(msg: String): Unit = errors = errors :+ msg
class StubUserRepo extends UserRepository:
private var users = Map.empty[Long, String]
private var nextId = 1L
var deletedIds: List[Long] = Nil
def findById(id: Long): Option[String] = users.get(id)
def save(name: String): Long =
val id = nextId; nextId += 1
users += (id -> name); id
def delete(id: Long): Boolean =
deletedIds = deletedIds :+ id
users -= id; true
class StubEmailSender extends EmailSender:
var sent: List[(String, String, String)] = Nil
var shouldFail = false
def send(to: String, subject: String, body: String): Boolean =
if shouldFail then false
else { sent = sent :+ (to, subject, body); true }
// Tests
class UserServiceSpec extends AnyFlatSpec with Matchers:
def createService() =
val repo = StubUserRepo()
val email = StubEmailSender()
val logger = StubLogger()
val clock = StubClock(1000L)
val service = UserService(repo, email, logger, clock)
(service, repo, email, logger)
"register" should "create user and send email" in {
val (service, repo, email, logger) = createService()
val result = service.register("Alice", "alice@test.com")
result shouldBe Right(1L)
email.sent should have length 1
email.sent.head._1 shouldBe "alice@test.com"
logger.infos should have length 1
logger.errors shouldBe empty
}
it should "reject empty name" in {
val (service, _, _, logger) = createService()
val result = service.register("", "test@test.com")
result shouldBe Left("Name is required")
logger.errors should have length 1
}
"deregister" should "delete existing user" in {
val (service, repo, _, logger) = createService()
repo.save("Alice")
val result = service.deregister(1)
result shouldBe Right(())
repo.deletedIds should contain(1L)
}
it should "fail for non-existent user" in {
val (service, _, _, _) = createService()
val result = service.deregister(99)
result shouldBe a[Left[?, ?]]
}Use Cases
- Unit testing with dependency injection
- Test doubles for external services
- Verifying side effects in tests
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalaintermediate
Unit Testing with ScalaTest
Write unit tests with ScalaTest: FunSuite, FlatSpec, matchers, mocking, and async testing.
Best for: Unit testing Scala code
#scala#testing
scalaintermediate
Property-Based Testing with ScalaCheck
Write property-based tests with ScalaCheck: generators, properties, shrinking, and custom Arbitrary.
Best for: Exhaustive testing with random inputs
#scala#testing
scalaintermediate
Testing with Specs2 Framework
Write readable tests with specs2: acceptance specs, matchers, data tables, and mock integration.
Best for: Behavior-driven development
#scala#testing
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics