Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
95: Mocking with ScalaMock
By now, you've probably realized that unit testing becomes a nightmare when your code depends on external systems—databases, third-party APIs, or slow legacy services. You don't want your test suite to fail just because the Stripe API is down or because your local database isn't configured. That's where ScalaMock comes in. It lets us create "fake" versions of our dependencies so we can isolate the logic we're actually trying to test.
How do I actually create a mock and define its behavior?
The core of ScalaMock is the mock[T] method. You use it to create an instance of a trait or class that behaves exactly how you tell it to. Let's say we're building a PaymentProcessor that depends on a PaymentGateway. We don't want to charge real credit cards during a test, obviously.
trait PaymentGateway {
def charge(amount: Double, currency: String): Boolean
}
class PaymentProcessor(gateway: PaymentGateway) {
def processPayment(amount: Double): String = {
if (gateway.charge(amount, "USD")) "Success" else "Failed"
}
}
// In your test:
val mockGateway = mock[PaymentGateway]
val processor = new PaymentProcessor(mockGateway)
// Tell the mock: "When charge is called with 100.0 and 'USD', return true"
(mockGateway.charge _).expects(100.0, "USD").returning(true)
val result = processor.processPayment(100.0)
assert(result == "Success")
Notice the (mockGateway.charge _) syntax. We're passing the method itself as a function to ScalaMock. I'll be honest: the first time I saw that syntax, it looked like a typo. But it's how ScalaMock intercepts the call to set up the expectation.
What if I don't care about the exact arguments passed in?
In the example above, the test would fail if the PaymentProcessor suddenly decided to charge 100.01 instead of 100.0. Sometimes, that's too rigid. If you only care that some double and some string were passed, you can use argument matchers like where or arg.
import org.scala-mock.ScalaMockMatchers._
// Accept any double and any string
(mockGateway.charge _).expects(where { (amount: Double) => amount > 0 }, where { (curr: String) => curr == "USD" }).returning(true)
// Or use a simpler wildcard if you really don't care
(mockGateway.charge _).expects(*, *).returning(true)
I usually recommend being as specific as possible. If you use * everywhere, you might miss a bug where your code is passing null or a negative number to your dependency. Use where when you have a specific condition to validate.
What is the difference between "expects" and "stub"?
This is a common point of confusion. expects is for verification. If you use expects and that method is never called during the test, ScalaMock will throw an exception and fail your test. It's saying, "I expected this to happen, but it didn't."
A stub, on the other hand, is just for behavior. Use a stub when the method call is necessary for the code to run, but you don't actually care if it gets called or how many times it's called. It just needs to return a value so the rest of the logic can proceed.
// This MUST be called exactly once, or the test fails
(mockGateway.charge _).expects(100.0, "USD").returning(true)
// This CAN be called, and if it is, it returns true. If not, no one cares.
(mockGateway.charge _).stub(where { (a: Double) => true }, where { (s: String) => true }).returns(true)
I generally use expects for the primary action I'm testing (the "side effect") and stub for the supporting data the system needs to function. It keeps the test failures focused on what actually matters.
📋 Practical Task
Exercise: Mocking a User Notification Workflow
You are working on a UserService that handles user registration. The service depends on a UserRepository to save the user and an EmailClient to send a welcome email.
The Goal: Write a test case using ScalaMock to ensure that the EmailClient is only called if the UserRepository successfully saves the user.
Requirements:
- Create a trait
UserRepositorywith a methodsave(user: User): Boolean. - Create a trait
EmailClientwith a methodsendWelcomeEmail(email: String): Unit. - Implement a
UserServiceclass that callsrepo.save(user). If it returnstrue, it should then callemailClient.sendWelcomeEmail(user.email). - Write a test where
UserRepository.savereturnsfalse. Verify thatEmailClient.sendWelcomeEmailis never called. - Write a test where
UserRepository.savereturnstrue. Verify thatEmailClient.sendWelcomeEmailis called exactly once with the correct email address.
There are no comments for now.